From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-m32114.qiye.163.com (mail-m32114.qiye.163.com [220.197.32.114]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A8465208A4; Tue, 13 Oct 2026 17:26:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.32.114 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791912368; cv=none; b=pOoSStJqAJeafIDJ0O4Ue78+w9b2yncwULLjUbFhcZKkJNRHHp08762124Z/JB2VqvdLi9nyzblaSrw/HSAzq8+kq7bEWX/E0D+Kb1RECm5STk4kNX1HU884TlHi3TOdPsg/TA9oC2W5I9+v0X174iGcaspF54fGttFvgcw3Ph0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791912368; c=relaxed/simple; bh=GMujcxGN1buV7GwiGOkTFHJeX+TenM3QpJT7oiacN30=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XdE0jqBYbpUrxi8VtpepA98CyJLYroH7ECbZM8q42sgkqVcI3NINmpqX+P+SOIOy5gf8SsWs03Q2UMp1800wuMvQkJK5hEMBE6USUSRXfPBbn1ko6UukpJOuTWoEGROlAbXiNvGq5DR71CVCnsl9KnKUaH7UQGAbcyflvrhEtjw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com; spf=pass smtp.mailfrom=rock-chips.com; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b=NcmsN45H; arc=none smtp.client-ip=220.197.32.114 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b="NcmsN45H" Received: from zyb-HP-ProDesk-680-G2-MT.. (unknown [61.154.14.86]) by smtp.qiye.163.com (Hmail) with ESMTP id 50e2b9413; Tue, 13 Oct 2026 20:49:57 +0800 (GMT+08:00) From: Damon Ding To: Sandy Huang , Heiko Stuebner , Andy Yan , David Airlie , Simona Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Vinod Koul , Andrzej Hajda , Robert Foss Cc: Neil Armstrong , Manivannan Sadhasivam , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Luca Ceresoli , Dave Jones , dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-phy@lists.infradead.org, Damon Ding Subject: [PATCH v1 4/6] phy: rockchip: Add support for Naneng eDP phy Date: Tue, 13 Oct 2026 20:49:44 +0800 Message-Id: <20261013124946.1807886-5-damon.ding@rock-chips.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20261013124946.1807886-1-damon.ding@rock-chips.com> References: <20261013124946.1807886-1-damon.ding@rock-chips.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-HM-Tid: 0aa1354ffdff03a8kunm2e7c128b3016 X-HM-MType: 1 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWRgWCB1ZQUpXWS1ZQUlXWQ8JGhUIEh9ZQVlCT00fVh9PGkhOSEtJHUtLQ1YVFA kWGhdVEwETFhoSFyQUDg9ZV1kYEgtZQVlNSlVKTk9VSk9VQ01ZV1kWGg8SFR0UWUFZT0tIVUpLSE pKQk1VSktLVUpCWQY+ DKIM-Signature: a=rsa-sha256; b=NcmsN45HTsHeGhImb0I1tdINDsgvVFxQfcZgJk+25mpSh9bl5m8bslBAc972Mjk/r5nwLPYUFM02gLbZ9fICWQDMgkDC+HHtsfolo4Sp/QhDeie8psj8tpjrJeQ8K+/E4JplyAV8Zp0IQrTiCur8hIIJUSTvspPGkmWRgr59oLo=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=MFxI5PRgVTs6YimYg/7rWn3Qmlj2MBU5yHt4uG0uFpM=; h=date:mime-version:subject:message-id:from; Add a driver for the Rockchip eDP transmitter PHY with NaNeng IP block, as found on the RK3568 SoC. The PHY registers live in the eDP PHY GRF, of which the PHY is a child node, so accesses are performed through the parent syscon regmap. 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 --- drivers/phy/rockchip/Kconfig | 10 + drivers/phy/rockchip/Makefile | 1 + .../phy/rockchip/phy-rockchip-naneng-edp.c | 435 ++++++++++++++++++ 3 files changed, 446 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 14698571b607..202a6973e20b 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -74,6 +74,16 @@ 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 + 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..a407714f639a --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-naneng-edp.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2026 Rockchip Electronics Co., Ltd. + * + * Author: Damon Ding + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 *grf; + struct device *dev; + struct clk *refclk; +}; + +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->grf, EDP_PHY_GRF_CON3, + FIELD_PREP_WM16(EDP_PHY_TX0_EMP_MASK, emp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON4, + FIELD_PREP_WM16(EDP_PHY_TX0_AMP_MASK, amp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON5, + FIELD_PREP_WM16(EDP_PHY_TX0_AMP_SCALE_MASK, amp_scale)); + break; + case 1: + regmap_write(edpphy->grf, EDP_PHY_GRF_CON3, + FIELD_PREP_WM16(EDP_PHY_TX1_EMP_MASK, emp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON4, + FIELD_PREP_WM16(EDP_PHY_TX1_AMP_MASK, amp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON5, + FIELD_PREP_WM16(EDP_PHY_TX1_AMP_SCALE_MASK, amp_scale)); + break; + case 2: + regmap_write(edpphy->grf, EDP_PHY_GRF_CON3, + FIELD_PREP_WM16(EDP_PHY_TX2_EMP_MASK, emp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON4, + FIELD_PREP_WM16(EDP_PHY_TX2_AMP_MASK, amp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON5, + FIELD_PREP_WM16(EDP_PHY_TX2_AMP_SCALE_MASK, amp_scale)); + break; + case 3: + regmap_write(edpphy->grf, EDP_PHY_GRF_CON3, + FIELD_PREP_WM16(EDP_PHY_TX3_EMP_MASK, emp)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON4, + FIELD_PREP_WM16(EDP_PHY_TX3_AMP_MASK, amp)); + regmap_write(edpphy->grf, 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 < dp->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->grf, 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->grf, EDP_PHY_GRF_CON5, FIELD_PREP_WM16(EDP_PHY_TX_MODE_MASK, 0x3)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON0, FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0x1)); + + switch (dp->link_rate) { + case 1620: + regmap_write(edpphy->grf, EDP_PHY_GRF_CON1, + FIELD_PREP_WM16(EDP_PHY_PLL_DIV_MASK, 0x4380)); + regmap_write(edpphy->grf, 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->grf, EDP_PHY_GRF_CON8, + FIELD_PREP_WM16(EDP_PHY_PLL_CTL_H_MASK, 0x0800)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON9, + FIELD_PREP_WM16(EDP_PHY_TX_CTL_MASK, 0x0000)); + break; + case 2700: + regmap_write(edpphy->grf, EDP_PHY_GRF_CON1, + FIELD_PREP_WM16(EDP_PHY_PLL_DIV_MASK, 0x3840)); + regmap_write(edpphy->grf, 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->grf, EDP_PHY_GRF_CON8, + FIELD_PREP_WM16(EDP_PHY_PLL_CTL_H_MASK, 0x0800)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON9, + FIELD_PREP_WM16(EDP_PHY_TX_CTL_MASK, 0x0000)); + break; + } + + if (dp->ssc) + regmap_write(edpphy->grf, 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->grf, EDP_PHY_GRF_CON6, + FIELD_PREP_WM16(EDP_PHY_SSC_EN_MASK, 0x0)); + + regmap_write(edpphy->grf, EDP_PHY_GRF_CON0, FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON0, + FIELD_PREP_WM16(EDP_PHY_TX_PD_MASK, ~GENMASK(dp->lanes - 1, 0))); + ret = regmap_read_poll_timeout(edpphy->grf, 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->grf, EDP_PHY_GRF_CON5, FIELD_PREP_WM16(EDP_PHY_TX_MODE_MASK, 0x0)); + regmap_write(edpphy->grf, 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. + */ + if (dp->set_voltages) { + /* Lane count verified previously. */ + for (i = 0; i < dp->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_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; + + regmap_read(edpphy->grf, 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->grf, 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->grf, 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->grf, 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->grf, 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->grf, 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->grf, 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->grf, EDP_PHY_GRF_CON5, FIELD_PREP_WM16(EDP_PHY_TX_MODE_MASK, 0x3)); + regmap_write(edpphy->grf, EDP_PHY_GRF_CON0, FIELD_PREP_WM16(EDP_PHY_PD_PLL_MASK, 0x1)); + regmap_write(edpphy->grf, 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 int rockchip_edp_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_edp_phy *edpphy; + struct phy *phy; + struct phy_provider *phy_provider; + + edpphy = devm_kzalloc(dev, sizeof(*edpphy), GFP_KERNEL); + if (!edpphy) + return -ENOMEM; + + edpphy->dev = dev; + + edpphy->grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(edpphy->grf)) + return dev_err_probe(dev, PTR_ERR(edpphy->grf), "Failed to get GRF syscon\n"); + + edpphy->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(edpphy->refclk)) + return dev_err_probe(dev, PTR_ERR(edpphy->refclk), "Failed to get refclk\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 "); +MODULE_DESCRIPTION("Rockchip Naneng eDP Transmitter PHY driver"); +MODULE_LICENSE("GPL"); -- 2.34.1