mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v5 0/3] Add eDP support for RK3568
@ 2026-11-10  7:45 Damon Ding
  2026-11-10  7:45 ` [PATCH v5 1/3] dt-bindings: phy: Add rockchip,rk3568-edp-phy Damon Ding
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Damon Ding @ 2026-11-10  7:45 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiko Stuebner
  Cc: Neil Armstrong, Manivannan Sadhasivam, Luca Ceresoli,
	Dmitry Baryshkov, Sebastian Reichel, Dave Jones, linux-phy,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel,
	Damon Ding

Picked from:
https://lore.kernel.org/all/20261030064905.2248999-1-damon.ding@rock-chips.com/

The 'rockchip,rk3568-edp' display binding, the RK3568 support for the
Analogix DP bridge driver and the RK3568 support for the Rockchip
Analogix DP driver have been applied to the drm-misc-next tree.

PATCH 1 is to document the RK3568 eDP Transmitter PHY with Naneng IP
        block, along with the eDP PHY GRF syscon node it resides in.
PATCH 2 is to add the driver for the Rockchip eDP Transmitter PHY with
        Naneng IP block.
PATCH 3 is to add the eDP controller and eDP PHY nodes for the RK3568
        SoCs.

Tested on RK3568 EVB1 board with LG eDP panel LP079QX1-SP0V.

&edp {
  avdd-0v9-supply = <&vdda0v9_image>;
  avdd-1v8-supply = <&vcca1v8_image>;
  force-hpd;
  status = "okay";

  aux-bus {
    panel {
      compatible = "edp-panel";
      backlight = <&backlight>;
      power-supply = <&vcc3v3_lcd0_n>;
      no-hpd;

      port {
        panel_in_edp: endpoint {
          remote-endpoint = <&edp_out_panel>;
        };
      };
    };
  };
};

&edp_in {
  edp_in_vp0: endpoint {
    remote-endpoint = <&vp0_out_edp>;
  };
};

&edp_out {
  edp_out_panel: endpoint {
    remote-endpoint = <&panel_in_edp>;
  };
};

&edp_phy {
  status = "okay";
};

&vp0 {
  vp0_out_edp: endpoint@ROCKCHIP_VOP2_EP_EDP0 {
    reg = <ROCKCHIP_VOP2_EP_EDP0>;
    remote-endpoint = <&edp_in_vp0>;
  };
};

Damon Ding (3):
  dt-bindings: phy: Add rockchip,rk3568-edp-phy
  phy: rockchip: Add support for Naneng eDP phy
  arm64: dts: rockchip: rk356x: Add eDP controller and phy nodes

 .../bindings/phy/rockchip,rk3568-edp-phy.yaml |  46 ++
 .../rockchip/rockchip,rk3568-edp-phy-grf.yaml |  80 +++
 arch/arm64/boot/dts/rockchip/rk356x-base.dtsi |  45 ++
 drivers/phy/rockchip/Kconfig                  |  11 +
 drivers/phy/rockchip/Makefile                 |   1 +
 .../phy/rockchip/phy-rockchip-naneng-edp.c    | 471 ++++++++++++++++++
 6 files changed, 654 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/rockchip,rk3568-edp-phy.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/rockchip/rockchip,rk3568-edp-phy-grf.yaml
 create mode 100644 drivers/phy/rockchip/phy-rockchip-naneng-edp.c

---

Changes in v2:
- Apply the lane-dependent TX configurations in set_lanes() instead of
  set_rate(), and store the lane count for set_voltages(), since the
  Analogix DP core issues separate phy_configure() calls and the lane
  count is only valid in the set_lanes/set_voltages calls. (Sashiko)
- Initialize val to zero in rockchip_edp_phy_enabled() to avoid using
  an uninitialized variable when regmap_read() fails. (Sashiko)
- Fail the probe when the chip data requires GRF configurations while
  the "rockchip,grf" property is missing. (Sashiko)
- Rebase onto latest drm-misc-next tree.

Changes in v3:
- Restore the PHY related changes which were missing in v2 for
  unexpected reasons.

Changes in v4:
- Restrict the avdd-0v9 and avdd-1v8 supplies to the RK3568 compatible
  in the Rockchip Analogix DP binding.
- Add the reg entry for the PHY node and the ranges/#address-cells/
  #size-cells for the parent syscon node in the Rockchip GRF schema.
- Map the PHY registers from its own reg property instead of accessing
  them through the parent syscon regmap.
- Model the PHY node with a reg property under the parent syscon in the
  DT.

Changes in v5:
- Move the APB clock (PCLK_EDPPHY_GRF) from the parent syscon node to
  the PHY node and enable it in the driver. (Sashiko)
- Split the "rockchip,rk3568-edp-phy-grf" syscon node to its own
  binding schema, instead of extending the shared Rockchip GRF schema.
- Validate the voltages against the effective lane count, to also
  cover combined set_lanes + set_voltages requests. (Sashiko)
- Select REGMAP_MMIO.
- Drop the example from the PHY binding schema, as complete MFD
  examples belong in the GRF binding schema.
- Rename the reference clock to "ref".

-- 
2.34.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v5 1/3] dt-bindings: phy: Add rockchip,rk3568-edp-phy
  2026-11-10  7:45 [PATCH v5 0/3] Add eDP support for RK3568 Damon Ding
@ 2026-11-10  7:45 ` Damon Ding
  2026-11-10  7:45 ` [PATCH v5 2/3] phy: rockchip: Add support for Naneng eDP phy Damon Ding
  2026-11-10  7:45 ` [PATCH v5 3/3] arm64: dts: rockchip: rk356x: Add eDP controller and phy nodes Damon Ding
  2 siblings, 0 replies; 4+ messages in thread
From: Damon Ding @ 2026-11-10  7:45 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiko Stuebner
  Cc: Neil Armstrong, Manivannan Sadhasivam, Luca Ceresoli,
	Dmitry Baryshkov, Sebastian Reichel, Dave Jones, linux-phy,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel,
	Damon Ding

Document the eDP transmitter PHY with Naneng IP block found on the
RK3568 SoC. The PHY has its own register window inside the eDP PHY GRF,
so its node is modeled as a child of the eDP PHY GRF syscon with a reg
property describing that window.

Also add the "rockchip,rk3568-edp-phy-grf" compatible with its own
binding schema, so the shared Rockchip GRF schema is not extended with
per-device conditionals.

Signed-off-by: Damon Ding <damon.ding@rock-chips.com>

---

Changes in v4:
- Add the reg entry for the PHY node and the ranges/#address-cells/
  #size-cells for the parent syscon node.

Changes in v5:
- Move the APB clock (PCLK_EDPPHY_GRF) from the parent syscon node to
  the PHY node, matching the updated driver.
- Split the "rockchip,rk3568-edp-phy-grf" syscon node to its own
  binding schema, instead of extending the shared Rockchip GRF schema.
- Rename the reference clock to "ref".
- Drop the example from the PHY binding schema, as complete MFD
  examples belong in the GRF binding schema.
---
 .../bindings/phy/rockchip,rk3568-edp-phy.yaml | 46 +++++++++++
 .../rockchip/rockchip,rk3568-edp-phy-grf.yaml | 80 +++++++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/rockchip,rk3568-edp-phy.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/rockchip/rockchip,rk3568-edp-phy-grf.yaml

diff --git a/Documentation/devicetree/bindings/phy/rockchip,rk3568-edp-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,rk3568-edp-phy.yaml
new file mode 100644
index 000000000000..06dd1d19dca2
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rockchip,rk3568-edp-phy.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/rockchip,rk3568-edp-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip eDP Transmitter PHY with Naneng IP block
+
+maintainers:
+  - Damon Ding <damon.ding@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+description:
+  On the RK3568 SoC the eDP PHY is controlled through the eDP PHY GRF
+  registers, therefore the PHY node is expected to be a child of the
+  eDP PHY GRF syscon node.
+
+properties:
+  compatible:
+    const: rockchip,rk3568-edp-phy
+
+  reg:
+    maxItems: 1
+    description: PHY registers within the eDP PHY GRF.
+
+  clocks:
+    items:
+      - description: Reference clock from the PMU CRU
+      - description: APB clock gating the PHY register window
+
+  clock-names:
+    items:
+      - const: ref
+      - const: apb
+
+  "#phy-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - "#phy-cells"
+
+additionalProperties: false
diff --git a/Documentation/devicetree/bindings/soc/rockchip/rockchip,rk3568-edp-phy-grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/rockchip,rk3568-edp-phy-grf.yaml
new file mode 100644
index 000000000000..852420798f06
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/rockchip/rockchip,rk3568-edp-phy-grf.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/rockchip/rockchip,rk3568-edp-phy-grf.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip eDP PHY GRF on the RK3568 SoC
+
+maintainers:
+  - Damon Ding <damon.ding@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+description:
+  The eDP PHY GRF is a dedicated register file on the RK3568 SoC,
+  holding the registers used by the eDP Transmitter PHY with Naneng IP
+  block. The PHY itself is modeled as a child node, with its reg
+  property describing the PHY register window inside the GRF.
+
+properties:
+  compatible:
+    items:
+      - const: rockchip,rk3568-edp-phy-grf
+      - const: syscon
+      - const: simple-mfd
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 1
+
+  ranges: true
+
+patternProperties:
+  "^phy@[0-9a-f]+$":
+    type: object
+    $ref: /schemas/phy/rockchip,rk3568-edp-phy.yaml#
+    unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rk3568-cru.h>
+
+    soc {
+      #address-cells = <2>;
+      #size-cells = <2>;
+
+      edp_phy_grf: syscon@fdcb0000 {
+        compatible = "rockchip,rk3568-edp-phy-grf", "syscon", "simple-mfd";
+        reg = <0x0 0xfdcb0000 0x0 0x8000>;
+        clocks = <&cru PCLK_EDPPHY_GRF>;
+        ranges = <0x0 0x0 0xfdcb0000 0x100>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+
+        edp_phy: phy@0 {
+          compatible = "rockchip,rk3568-edp-phy";
+          reg = <0x0 0x100>;
+          clocks = <&pmucru XIN_OSC0_EDPPHY_G>, <&cru PCLK_EDPPHY_GRF>;
+          clock-names = "ref", "apb";
+          #phy-cells = <0>;
+        };
+      };
+    };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v5 2/3] phy: rockchip: Add support for Naneng eDP phy
  2026-11-10  7:45 [PATCH v5 0/3] Add eDP support for RK3568 Damon Ding
  2026-11-10  7:45 ` [PATCH v5 1/3] dt-bindings: phy: Add rockchip,rk3568-edp-phy Damon Ding
@ 2026-11-10  7:45 ` Damon Ding
  2026-11-10  7:45 ` [PATCH v5 3/3] arm64: dts: rockchip: rk356x: Add eDP controller and phy nodes Damon Ding
  2 siblings, 0 replies; 4+ messages in thread
From: Damon Ding @ 2026-11-10  7:45 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiko Stuebner
  Cc: Neil Armstrong, Manivannan Sadhasivam, Luca Ceresoli,
	Dmitry Baryshkov, Sebastian Reichel, Dave Jones, linux-phy,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel,
	Damon Ding

Add a driver for the Rockchip eDP transmitter PHY with Naneng IP
block, as found on the RK3568 SoC.

The PHY has its own register window inside the eDP PHY GRF, so its
node is modeled as a child of the eDP PHY GRF syscon with a reg
property describing that window, and the driver maps that window
directly.  The PHY supports 1/2/4 lanes at 1.62 Gbps and 2.7 Gbps,
optional spread spectrum clocking, as well as per-lane voltage swing
and pre-emphasis tuning, all exposed through the generic PHY
DisplayPort configuration interface.

Signed-off-by: Damon Ding <damon.ding@rock-chips.com>

---

Changes in v2:
- Apply the lane-dependent TX configurations in set_lanes() instead of
  set_rate(), and store the lane count for set_voltages(), since the
  Analogix DP core issues separate phy_configure() calls and the lane
  count is only valid in the set_lanes/set_voltages calls. (Sashiko)
- Initialize val to zero in rockchip_edp_phy_enabled() to avoid using
  an uninitialized variable when regmap_read() fails. (Sashiko)

Changes in v3:
- Add the changes which were missing in v2 for unexpected reasons.

Changes in v4:
- Map the PHY registers from its own reg property instead of
  accessing them through the parent syscon regmap.

Changes in v5:
- Move the APB clock (PCLK_EDPPHY_GRF) from the parent syscon node to
  the PHY node and enable it in the driver. (Sashiko)
- Validate the voltages against the effective lane count, to also
  cover combined set_lanes + set_voltages requests. (Sashiko)
- Select REGMAP_MMIO. (Sashiko)
- Rename the reference clock to "ref", following the review of the
  binding.
---
 drivers/phy/rockchip/Kconfig                  |  11 +
 drivers/phy/rockchip/Makefile                 |   1 +
 .../phy/rockchip/phy-rockchip-naneng-edp.c    | 471 ++++++++++++++++++
 3 files changed, 483 insertions(+)
 create mode 100644 drivers/phy/rockchip/phy-rockchip-naneng-edp.c

diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 39759bb2fa1d..efa49d6b620d 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -74,6 +74,17 @@ config PHY_ROCKCHIP_NANENG_COMBO_PHY
 	  Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII
 	  combo PHY with NaNeng IP block.
 
+config PHY_ROCKCHIP_NANENG_EDP
+	tristate "Rockchip Naneng eDP Transmitter PHY driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Rockchip eDP Transmitter PHY with
+	  Naneng IP block. The PHY, which is controlled through the eDP
+	  PHY GRF, provides 1/2/4 lanes running at 1.62/2.7 Gbps per lane
+	  for the eDP controller found on the RK3568 SoC.
+
 config PHY_ROCKCHIP_PCIE
 	tristate "Rockchip PCIe PHY Driver"
 	depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index 117aaffd037d..260a10157461 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY)	+= phy-rockchip-inno-dsidphy.o
 obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI)	+= phy-rockchip-inno-hdmi.o
 obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY)	+= phy-rockchip-naneng-combphy.o
+obj-$(CONFIG_PHY_ROCKCHIP_NANENG_EDP)	+= phy-rockchip-naneng-edp.o
 obj-$(CONFIG_PHY_ROCKCHIP_PCIE)		+= phy-rockchip-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_DCPHY)	+= phy-rockchip-samsung-dcphy.o
 obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX)	+= phy-rockchip-samsung-hdptx.o
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-edp.c b/drivers/phy/rockchip/phy-rockchip-naneng-edp.c
new file mode 100644
index 000000000000..7f0ecc347c78
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-edp.c
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2026 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Damon Ding <damon.ding@rock-chips.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/hw_bitfield.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define EDP_PHY_GRF_CON0		0x0000
+#define EDP_PHY_TX_IDLE_MASK		GENMASK(11, 8)
+#define EDP_PHY_TX_PD_MASK		GENMASK(7, 4)
+#define EDP_PHY_IDDQ_EN_MASK		BIT(1)
+#define EDP_PHY_PD_PLL_MASK		BIT(0)
+
+#define EDP_PHY_GRF_CON1		0x0004
+#define EDP_PHY_PLL_DIV_MASK		GENMASK(14, 0)
+
+#define EDP_PHY_GRF_CON2		0x0008
+#define EDP_PHY_TX_RTERM_MASK		GENMASK(10, 8)
+#define EDP_PHY_RATE_MASK		GENMASK(5, 4)
+#define EDP_PHY_REF_DIV_MASK		GENMASK(3, 0)
+
+#define EDP_PHY_GRF_CON3		0x000c
+#define EDP_PHY_TX3_EMP_MASK		GENMASK(15, 12)
+#define EDP_PHY_TX2_EMP_MASK		GENMASK(11, 8)
+#define EDP_PHY_TX1_EMP_MASK		GENMASK(7, 4)
+#define EDP_PHY_TX0_EMP_MASK		GENMASK(3, 0)
+
+#define EDP_PHY_GRF_CON4		0x0010
+#define EDP_PHY_TX3_AMP_MASK		GENMASK(14, 12)
+#define EDP_PHY_TX2_AMP_MASK		GENMASK(10, 8)
+#define EDP_PHY_TX1_AMP_MASK		GENMASK(6, 4)
+#define EDP_PHY_TX0_AMP_MASK		GENMASK(2, 0)
+
+#define EDP_PHY_GRF_CON5		0x0014
+#define EDP_PHY_TX_MODE_MASK		GENMASK(9, 8)
+#define EDP_PHY_TX3_AMP_SCALE_MASK	GENMASK(7, 6)
+#define EDP_PHY_TX2_AMP_SCALE_MASK	GENMASK(5, 4)
+#define EDP_PHY_TX1_AMP_SCALE_MASK	GENMASK(3, 2)
+#define EDP_PHY_TX0_AMP_SCALE_MASK	GENMASK(1, 0)
+
+#define EDP_PHY_GRF_CON6		0x0018
+#define EDP_PHY_SSC_DEPTH_MASK		GENMASK(15, 12)
+#define EDP_PHY_SSC_EN_MASK		BIT(11)
+#define EDP_PHY_SSC_CNT_MASK		GENMASK(9, 0)
+
+#define EDP_PHY_GRF_CON7		0x001c
+
+#define EDP_PHY_GRF_CON8		0x0020
+#define EDP_PHY_PLL_CTL_H_MASK		GENMASK(15, 0)
+
+#define EDP_PHY_GRF_CON9		0x0024
+#define EDP_PHY_TX_CTL_MASK		GENMASK(15, 0)
+
+#define EDP_PHY_GRF_CON10		0x0028
+#define EDP_PHY_AUX_RCV_PD_SEL_MASK	BIT(5)
+#define EDP_PHY_AUX_DRV_PD_SEL_MASK	BIT(4)
+#define EDP_PHY_AUX_IDLE_MASK		BIT(2)
+#define EDP_PHY_AUX_RCV_PD_MASK		BIT(1)
+#define EDP_PHY_AUX_DRV_PD_MASK		BIT(0)
+
+#define EDP_PHY_GRF_CON11		0x002c
+#define EDP_PHY_AUX_RCV_VCM_MASK	GENMASK(14, 12)
+#define EDP_PHY_AUX_MODE_MASK		GENMASK(11, 10)
+#define EDP_PHY_AUX_AMP_SCALE_MASK	GENMASK(9, 8)
+#define EDP_PHY_AUX_AMP_MASK		GENMASK(6, 4)
+#define EDP_PHY_AUX_RTERM_MASK		GENMASK(2, 0)
+
+#define EDP_PHY_GRF_STATUS0		0x0030
+#define PLL_RDY				BIT(0)
+
+#define EDP_PHY_GRF_STATUS1		0x0034
+
+struct rockchip_edp_phy {
+	struct regmap *regmap;
+	struct device *dev;
+	struct clk *refclk;
+	u8 lanes;
+};
+
+static struct {
+	int amp;
+	int amp_scale;
+	int emp;
+} swing_pre_emph_table[4][4] = {
+	{ {0x1, 0x1, 0x0}, {0x2, 0x1, 0x4}, {0x3, 0x1, 0x8}, {0x4, 0x1, 0xd} },
+	{ {0x3, 0x1, 0x0}, {0x5, 0x1, 0x7}, {0x6, 0x1, 0x6}, { -1,  -1,  -1} },
+	{ {0x5, 0x1, 0x0}, {0x7, 0x1, 0x4}, { -1,  -1,  -1}, { -1,  -1,  -1} },
+	{ {0x7, 0x1, 0x0}, { -1,  -1,  -1}, { -1,  -1,  -1}, { -1,  -1,  -1} },
+};
+
+static void rockchip_edp_phy_set_voltage(struct rockchip_edp_phy *edpphy,
+					 struct phy_configure_opts_dp *dp,
+					 u8 lane)
+{
+	u32 amp, amp_scale, emp;
+
+	amp = swing_pre_emph_table[dp->voltage[lane]][dp->pre[lane]].amp;
+	amp_scale = swing_pre_emph_table[dp->voltage[lane]][dp->pre[lane]].amp_scale;
+	emp = swing_pre_emph_table[dp->voltage[lane]][dp->pre[lane]].emp;
+
+	switch (lane) {
+	case 0:
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON3,
+			     FIELD_PREP_WM16(EDP_PHY_TX0_EMP_MASK, emp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON4,
+			     FIELD_PREP_WM16(EDP_PHY_TX0_AMP_MASK, amp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5,
+			     FIELD_PREP_WM16(EDP_PHY_TX0_AMP_SCALE_MASK, amp_scale));
+		break;
+	case 1:
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON3,
+			     FIELD_PREP_WM16(EDP_PHY_TX1_EMP_MASK, emp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON4,
+			     FIELD_PREP_WM16(EDP_PHY_TX1_AMP_MASK, amp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5,
+			     FIELD_PREP_WM16(EDP_PHY_TX1_AMP_SCALE_MASK, amp_scale));
+		break;
+	case 2:
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON3,
+			     FIELD_PREP_WM16(EDP_PHY_TX2_EMP_MASK, emp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON4,
+			     FIELD_PREP_WM16(EDP_PHY_TX2_AMP_MASK, amp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5,
+			     FIELD_PREP_WM16(EDP_PHY_TX2_AMP_SCALE_MASK, amp_scale));
+		break;
+	case 3:
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON3,
+			     FIELD_PREP_WM16(EDP_PHY_TX3_EMP_MASK, emp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON4,
+			     FIELD_PREP_WM16(EDP_PHY_TX3_AMP_MASK, amp));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5,
+			     FIELD_PREP_WM16(EDP_PHY_TX3_AMP_SCALE_MASK, amp_scale));
+		break;
+	}
+}
+
+static int rockchip_edp_phy_set_voltages(struct rockchip_edp_phy *edpphy,
+					 struct phy_configure_opts_dp *dp)
+{
+	u8 lane;
+
+	for (lane = 0; lane < edpphy->lanes; lane++)
+		rockchip_edp_phy_set_voltage(edpphy, dp, lane);
+
+	return 0;
+}
+
+static int rockchip_edp_phy_set_rate(struct rockchip_edp_phy *edpphy,
+				     struct phy_configure_opts_dp *dp)
+{
+	u32 value;
+	int ret;
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0,
+		     FIELD_PREP_WM16(EDP_PHY_TX_IDLE_MASK, 0xf) |
+		     FIELD_PREP_WM16(EDP_PHY_TX_PD_MASK, 0xf));
+	usleep_range(100, 101);
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5, FIELD_PREP_WM16(EDP_PHY_TX_MODE_MASK, 0x3));
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0, FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0x1));
+
+	switch (dp->link_rate) {
+	case 1620:
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON1,
+			     FIELD_PREP_WM16(EDP_PHY_PLL_DIV_MASK, 0x4380));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON2,
+			     FIELD_PREP_WM16(EDP_PHY_TX_RTERM_MASK, 0x1) |
+			     FIELD_PREP_WM16(EDP_PHY_RATE_MASK, 0x1) |
+			     FIELD_PREP_WM16(EDP_PHY_REF_DIV_MASK, 0x0));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON8,
+			     FIELD_PREP_WM16(EDP_PHY_PLL_CTL_H_MASK, 0x0800));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON9,
+			     FIELD_PREP_WM16(EDP_PHY_TX_CTL_MASK, 0x0000));
+		break;
+	case 2700:
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON1,
+			     FIELD_PREP_WM16(EDP_PHY_PLL_DIV_MASK, 0x3840));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON2,
+			     FIELD_PREP_WM16(EDP_PHY_TX_RTERM_MASK, 0x1) |
+			     FIELD_PREP_WM16(EDP_PHY_RATE_MASK, 0x0) |
+			     FIELD_PREP_WM16(EDP_PHY_REF_DIV_MASK, 0x0));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON8,
+			     FIELD_PREP_WM16(EDP_PHY_PLL_CTL_H_MASK, 0x0800));
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON9,
+			     FIELD_PREP_WM16(EDP_PHY_TX_CTL_MASK, 0x0000));
+		break;
+	}
+
+	if (dp->ssc)
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON6,
+			     FIELD_PREP_WM16(EDP_PHY_SSC_DEPTH_MASK, 0x9) |
+			     FIELD_PREP_WM16(EDP_PHY_SSC_EN_MASK, 0x1) |
+			     FIELD_PREP_WM16(EDP_PHY_SSC_CNT_MASK, 0x17d));
+	else
+		regmap_write(edpphy->regmap, EDP_PHY_GRF_CON6,
+			     FIELD_PREP_WM16(EDP_PHY_SSC_EN_MASK, 0x0));
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0, FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0));
+	ret = regmap_read_poll_timeout(edpphy->regmap, EDP_PHY_GRF_STATUS0,
+				       value, value & PLL_RDY, 100, 1000);
+	if (ret) {
+		dev_err(edpphy->dev, "Failed to wait for PHY PLL ready: %d\n", ret);
+		return ret;
+	}
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5, FIELD_PREP_WM16(EDP_PHY_TX_MODE_MASK, 0x0));
+
+	return 0;
+}
+
+static int rockchip_edp_phy_set_lanes(struct rockchip_edp_phy *edpphy,
+				      struct phy_configure_opts_dp *dp)
+{
+	edpphy->lanes = dp->lanes;
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0,
+		     FIELD_PREP_WM16(EDP_PHY_TX_PD_MASK, ~GENMASK(dp->lanes - 1, 0)));
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0,
+		     FIELD_PREP_WM16(EDP_PHY_TX_IDLE_MASK, ~GENMASK(dp->lanes - 1, 0)));
+
+	return 0;
+}
+
+static int rockchip_edp_phy_verify_config(struct rockchip_edp_phy *edpphy,
+					  struct phy_configure_opts_dp *dp)
+{
+	int i;
+
+	/* If changing link rate was required, verify it's supported. */
+	if (dp->set_rate) {
+		switch (dp->link_rate) {
+		case 1620:
+		case 2700:
+			/* valid bit rate */
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (dp->set_lanes) {
+		/* Verify lane count. */
+		switch (dp->lanes) {
+		case 1:
+		case 2:
+		case 4:
+			/* valid lane count. */
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * If changing voltages is required, check swing and pre-emphasis
+	 * levels, per-lane.  The effective lane count is dp->lanes when
+	 * set_lanes is requested, or the previously stored count otherwise.
+	 */
+	if (dp->set_voltages) {
+		u8 lanes = dp->set_lanes ? dp->lanes : edpphy->lanes;
+
+		for (i = 0; i < lanes; i++) {
+			if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+				return -EINVAL;
+
+			/*
+			 * Sum of voltage swing and pre-emphasis levels cannot
+			 * exceed 3.
+			 */
+			if (dp->voltage[i] + dp->pre[i] > 3)
+				return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int rockchip_edp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct rockchip_edp_phy *edpphy = phy_get_drvdata(phy);
+	int ret;
+
+	ret = rockchip_edp_phy_verify_config(edpphy, &opts->dp);
+	if (ret) {
+		dev_err(edpphy->dev, "Invalid params for phy configure\n");
+		return ret;
+	}
+
+	if (opts->dp.set_rate) {
+		ret = rockchip_edp_phy_set_rate(edpphy, &opts->dp);
+		if (ret) {
+			dev_err(edpphy->dev, "Failed to set rate\n");
+			return ret;
+		}
+	}
+
+	if (opts->dp.set_lanes) {
+		ret = rockchip_edp_phy_set_lanes(edpphy, &opts->dp);
+		if (ret) {
+			dev_err(edpphy->dev, "Failed to set lanes\n");
+			return ret;
+		}
+	}
+
+	if (opts->dp.set_voltages) {
+		ret = rockchip_edp_phy_set_voltages(edpphy, &opts->dp);
+		if (ret) {
+			dev_err(edpphy->dev, "Failed to set voltages\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static bool rockchip_edp_phy_enabled(struct rockchip_edp_phy *edpphy)
+{
+	u32 val = 0;
+
+	regmap_read(edpphy->regmap, EDP_PHY_GRF_STATUS0, &val);
+
+	return FIELD_GET(PLL_RDY, val);
+}
+
+static int rockchip_edp_phy_power_on(struct phy *phy)
+{
+	struct rockchip_edp_phy *edpphy = phy_get_drvdata(phy);
+	int ret;
+
+	ret = clk_prepare_enable(edpphy->refclk);
+	if (ret < 0) {
+		dev_err(edpphy->dev, "Failed to prepare_enable refclk [%d]\n", ret);
+		return ret;
+	}
+
+	if (rockchip_edp_phy_enabled(edpphy))
+		return 0;
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON10,
+		     FIELD_PREP_WM16(EDP_PHY_AUX_RCV_PD_MASK, 0x1) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_DRV_PD_MASK, 0x1) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_IDLE_MASK, 0x1));
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0,
+		     FIELD_PREP_WM16(EDP_PHY_TX_IDLE_MASK, 0xf) |
+		     FIELD_PREP_WM16(EDP_PHY_TX_PD_MASK, 0xf) |
+		     FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0x1));
+	usleep_range(100, 101);
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON11,
+		     FIELD_PREP_WM16(EDP_PHY_AUX_RCV_VCM_MASK, 0x4) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_MODE_MASK, 0x1) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_AMP_SCALE_MASK, 0x1) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_AMP_MASK, 0x3) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_RTERM_MASK, 0x1));
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON10,
+		     FIELD_PREP_WM16(EDP_PHY_AUX_RCV_PD_MASK, 0x0) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_DRV_PD_MASK, 0x0));
+	usleep_range(100, 101);
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON10,
+		     FIELD_PREP_WM16(EDP_PHY_AUX_IDLE_MASK, 0x0));
+	usleep_range(10000, 11000);
+
+	return 0;
+}
+
+static int rockchip_edp_phy_power_off(struct phy *phy)
+{
+	struct rockchip_edp_phy *edpphy = phy_get_drvdata(phy);
+
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0,
+		     FIELD_PREP_WM16(EDP_PHY_TX_IDLE_MASK, 0xf) |
+		     FIELD_PREP_WM16(EDP_PHY_TX_PD_MASK, 0xf));
+	usleep_range(100, 101);
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON5, FIELD_PREP_WM16(EDP_PHY_TX_MODE_MASK, 0x3));
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON0, FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0x1));
+	regmap_write(edpphy->regmap, EDP_PHY_GRF_CON10,
+		     FIELD_PREP_WM16(EDP_PHY_AUX_RCV_PD_MASK, 0x1) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_DRV_PD_MASK, 0x1) |
+		     FIELD_PREP_WM16(EDP_PHY_AUX_IDLE_MASK, 0x1));
+
+	clk_disable_unprepare(edpphy->refclk);
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_edp_phy_ops = {
+	.power_on = rockchip_edp_phy_power_on,
+	.power_off = rockchip_edp_phy_power_off,
+	.configure = rockchip_edp_phy_configure,
+	.owner = THIS_MODULE,
+};
+
+static const struct regmap_config rockchip_edp_phy_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = EDP_PHY_GRF_STATUS1,
+};
+
+static int rockchip_edp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_edp_phy *edpphy;
+	void __iomem *regs;
+	struct clk *clk;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+
+	edpphy = devm_kzalloc(dev, sizeof(*edpphy), GFP_KERNEL);
+	if (!edpphy)
+		return -ENOMEM;
+
+	edpphy->dev = dev;
+
+	regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+	edpphy->regmap = devm_regmap_init_mmio(dev, regs, &rockchip_edp_phy_regmap_config);
+	if (IS_ERR(edpphy->regmap))
+		return dev_err_probe(dev, PTR_ERR(edpphy->regmap), "Failed to init regmap\n");
+
+	clk = devm_clk_get_enabled(dev, "apb");
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "Failed to get apb clock\n");
+
+	edpphy->refclk = devm_clk_get(dev, "ref");
+	if (IS_ERR(edpphy->refclk))
+		return dev_err_probe(dev, PTR_ERR(edpphy->refclk), "Failed to get reference clk\n");
+
+	phy = devm_phy_create(dev, NULL, &rockchip_edp_phy_ops);
+	if (IS_ERR(phy))
+		return dev_err_probe(dev, PTR_ERR(phy), "Failed to create PHY\n");
+
+	phy_set_drvdata(phy, edpphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider))
+		return dev_err_probe(dev, PTR_ERR(phy_provider),
+				     "Failed to register PHY provider\n");
+
+	return 0;
+}
+
+static const struct of_device_id rockchip_edp_phy_of_match[] = {
+	{ .compatible = "rockchip,rk3568-edp-phy", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_edp_phy_of_match);
+
+static struct platform_driver rockchip_edp_phy_driver = {
+	.driver = {
+		.name = "rockchip-edpphy-naneng",
+		.of_match_table	= of_match_ptr(rockchip_edp_phy_of_match),
+	},
+	.probe = rockchip_edp_phy_probe,
+};
+module_platform_driver(rockchip_edp_phy_driver);
+
+MODULE_AUTHOR("Damon Ding <damon.ding@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Naneng eDP Transmitter PHY driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v5 3/3] arm64: dts: rockchip: rk356x: Add eDP controller and phy nodes
  2026-11-10  7:45 [PATCH v5 0/3] Add eDP support for RK3568 Damon Ding
  2026-11-10  7:45 ` [PATCH v5 1/3] dt-bindings: phy: Add rockchip,rk3568-edp-phy Damon Ding
  2026-11-10  7:45 ` [PATCH v5 2/3] phy: rockchip: Add support for Naneng eDP phy Damon Ding
@ 2026-11-10  7:45 ` Damon Ding
  2 siblings, 0 replies; 4+ messages in thread
From: Damon Ding @ 2026-11-10  7:45 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiko Stuebner
  Cc: Neil Armstrong, Manivannan Sadhasivam, Luca Ceresoli,
	Dmitry Baryshkov, Sebastian Reichel, Dave Jones, linux-phy,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel,
	Damon Ding

Add the eDP PHY GRF (with the eDP PHY child node mapped by its own
register window) and the eDP controller nodes for the RK3568/RK3566
SoCs.

Signed-off-by: Damon Ding <damon.ding@rock-chips.com>

---

Changes in v4:
- Model the PHY node with a reg property under the parent syscon.

Changes in v5:
- Move the APB clock (PCLK_EDPPHY_GRF) from the parent syscon node to
  the PHY node, matching the updated binding and driver. (Sashiko)
- Rename the PHY reference clock to "ref", matching the binding.
---
 arch/arm64/boot/dts/rockchip/rk356x-base.dtsi | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
index 1a4562df20ee..e3f370cad5d5 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
@@ -402,6 +402,24 @@ usb2phy1_grf: syscon@fdca8000 {
 		reg = <0x0 0xfdca8000 0x0 0x8000>;
 	};
 
+	edp_phy_grf: syscon@fdcb0000 {
+		compatible = "rockchip,rk3568-edp-phy-grf", "syscon", "simple-mfd";
+		reg = <0x0 0xfdcb0000 0x0 0x8000>;
+		clocks = <&cru PCLK_EDPPHY_GRF>;
+		ranges = <0x0 0x0 0xfdcb0000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		edp_phy: phy@0 {
+			compatible = "rockchip,rk3568-edp-phy";
+			reg = <0x0 0x100>;
+			clocks = <&pmucru XIN_OSC0_EDPPHY_G>, <&cru PCLK_EDPPHY_GRF>;
+			clock-names = "ref", "apb";
+			#phy-cells = <0>;
+			status = "disabled";
+		};
+	};
+
 	pmucru: clock-controller@fdd00000 {
 		compatible = "rockchip,rk3568-pmucru";
 		reg = <0x0 0xfdd00000 0x0 0x1000>;
@@ -878,6 +896,33 @@ hdmi_out: port@1 {
 		};
 	};
 
+	edp: edp@fe0c0000 {
+		compatible = "rockchip,rk3568-edp";
+		reg = <0x0 0xfe0c0000 0x0 0x10000>;
+		clocks = <&pmucru XIN_OSC0_EDPPHY_G>, <&cru PCLK_EDP_CTRL>, <&cru HCLK_VO>;
+		clock-names = "dp", "pclk", "hclk";
+		interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&edp_phy>;
+		phy-names = "dp";
+		power-domains = <&power RK3568_PD_VO>;
+		resets = <&cru SRST_EDP_24M>, <&cru SRST_P_EDP_CTRL>;
+		reset-names = "dp", "apb";
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			edp_in: port@0 {
+				reg = <0>;
+			};
+
+			edp_out: port@1 {
+				reg = <1>;
+			};
+		};
+	};
+
 	qos_gpu: qos@fe128000 {
 		compatible = "rockchip,rk3568-qos", "syscon";
 		reg = <0x0 0xfe128000 0x0 0x20>;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-10  7:45 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10  7:45 [PATCH v5 0/3] Add eDP support for RK3568 Damon Ding
2026-11-10  7:45 ` [PATCH v5 1/3] dt-bindings: phy: Add rockchip,rk3568-edp-phy Damon Ding
2026-11-10  7:45 ` [PATCH v5 2/3] phy: rockchip: Add support for Naneng eDP phy Damon Ding
2026-11-10  7:45 ` [PATCH v5 3/3] arm64: dts: rockchip: rk356x: Add eDP controller and phy nodes Damon Ding

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®