Devicetree
 help / color / mirror / Atom feed
From: Cody Kang via B4 Relay <devnull+codykang.hk.gmail.com@kernel.org>
To: David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
	 Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	 Maxime Ripard <mripard@kernel.org>,
	Thomas Zimmermann <tzimmermann@suse.de>,
	 Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	 Conor Dooley <conor+dt@kernel.org>, Yixun Lan <dlan@kernel.org>,
	 Vinod Koul <vkoul@kernel.org>,
	Neil Armstrong <neil.armstrong@linaro.org>,
	 Haylen Chu <heylenay@4d2.org>,
	Michael Turquette <mturquette@baylibre.com>,
	 Stephen Boyd <sboyd@kernel.org>,
	Brian Masney <bmasney@redhat.com>,
	 Philipp Zabel <p.zabel@pengutronix.de>,
	Paul Walmsley <pjw@kernel.org>,
	 Palmer Dabbelt <palmer@dabbelt.com>,
	Albert Ou <aou@eecs.berkeley.edu>,
	 Alexandre Ghiti <alex@ghiti.fr>
Cc: dri-devel@lists.freedesktop.org, linux-riscv@lists.infradead.org,
	 devicetree@vger.kernel.org, spacemit@lists.linux.dev,
	 linux-kernel@vger.kernel.org, linux-phy@lists.infradead.org,
	 linux-clk@vger.kernel.org, Cody Kang <codykang.hk@gmail.com>
Subject: [PATCH RESEND 05/17] phy: spacemit: add Innosilicon DP TX PHY driver
Date: Sat, 25 Jul 2026 00:51:14 -0400	[thread overview]
Message-ID: <20260725-k3-display-v1-5-6de34d80e86c@gmail.com> (raw)
In-Reply-To: <20260725-k3-display-v1-0-6de34d80e86c@gmail.com>

From: Cody Kang <codykang.hk@gmail.com>

The PHY's registers are interleaved into its parent DP controller's MMIO
window rather than a window of its own, so it probes as a child of the
controller, carries no reg or reset, and reaches its registers through
the controller's regmap.

It registers its pixel PLL as a clock provider. That lets the display
controller ask for a mode's pixel rate through clk_set_rate(), and it
lets the APMU pixel-clock mux parent to the PHY through the clock
framework. Without it the controller and PHY would have to know each
other's registers.

Signed-off-by: Cody Kang <codykang.hk@gmail.com>
---
 drivers/phy/spacemit/Kconfig          |  13 +
 drivers/phy/spacemit/Makefile         |   1 +
 drivers/phy/spacemit/phy-k3-inno-dp.c | 948 ++++++++++++++++++++++++++++++++++
 3 files changed, 962 insertions(+)

diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
index 50b0005acf66..098f33998f73 100644
--- a/drivers/phy/spacemit/Kconfig
+++ b/drivers/phy/spacemit/Kconfig
@@ -23,3 +23,16 @@ config PHY_SPACEMIT_K1_USB2
 	help
 	  Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
 	  enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
+
+config PHY_SPACEMIT_K3_INNO_DP
+	tristate "SpacemiT K3 Innosilicon DisplayPort PHY driver"
+	depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
+	depends on COMMON_CLK
+	select GENERIC_PHY
+	select REGMAP
+	help
+	  Enable support for the Innosilicon DisplayPort transmit PHY
+	  integrated in the SpacemiT K3 SoC. The PHY shares its MMIO window
+	  with its parent DP/eDP controller (drivers/gpu/drm/spacemit/) and
+	  exposes lane / rate / vswing / pre-emphasis control through the
+	  generic PHY framework.
diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
index a821a21d6142..960c5827f5e5 100644
--- a/drivers/phy/spacemit/Makefile
+++ b/drivers/phy/spacemit/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)		+= phy-k1-pcie.o
 obj-$(CONFIG_PHY_SPACEMIT_K1_USB2)		+= phy-k1-usb2.o
+obj-$(CONFIG_PHY_SPACEMIT_K3_INNO_DP)		+= phy-k3-inno-dp.o
diff --git a/drivers/phy/spacemit/phy-k3-inno-dp.c b/drivers/phy/spacemit/phy-k3-inno-dp.c
new file mode 100644
index 000000000000..9dd62edb7a5a
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k3-inno-dp.c
@@ -0,0 +1,948 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SpacemiT K3 Innosilicon DisplayPort PHY driver.
+ *
+ * Copyright (C) 2025-2026 SpacemiT Co., Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-dp.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* Offsets are into the parent controller's MMIO window */
+#define DPTX_PHY_CTRL			0x0100
+#define DPTX_PHY_CTRL_XMIT_EN		GENMASK(20, 17)
+#define DPTX_PHY_CTRL_NUM_LANES		GENMASK(6, 5)
+#define DPTX_PHY_CTRL_RATE		GENMASK(1, 0)
+
+/* One 6-bit slot per lane; only the low 4 bits of each slot are used. */
+#define DPTX_PHY_LANE_TRIM		0x0104
+#define DPTX_PHY_LANE_BITS_PER		6
+#define DPTX_PHY_LANE_FIELD_MASK	0xf	/* preemp[1:0] | vswing[1:0] */
+#define DPTX_PHY_LANE_VSWING_SHIFT	2	/* within the lane field */
+
+/* MPLL is the link PLL (lane rate) */
+#define DPTX_ANA_MPLL			0x0180
+#define DPTX_ANA_MPLL_FBDIV_LBIT	GENMASK(31, 24)
+#define DPTX_ANA_MPLL_FBDIV_HBIT	GENMASK(19, 16)
+#define DPTX_ANA_MPLL_PREDIV		GENMASK(13, 8)
+#define DPTX_ANA_MPLL_LOCKED		BIT(7)
+#define DPTX_ANA_MPLL_DACPD		BIT(5)
+#define DPTX_ANA_MPLL_DSMPD		BIT(4)
+#define DPTX_ANA_MPLL_PD		BIT(0)
+
+#define DPTX_ANA_MPLL_FRAC		0x0184
+#define DPTX_ANA_MPLL_FRAC_LBIT		GENMASK(23, 16)
+#define DPTX_ANA_MPLL_FRAC_MBIT		GENMASK(15, 8)
+#define DPTX_ANA_MPLL_FRAC_HBIT		GENMASK(7, 0)
+
+#define DPTX_ANA_MPLL_DIV		0x0188
+#define DPTX_ANA_MPLL_VCOCLK_DIV8_EN	BIT(16)
+#define DPTX_ANA_MPLL_POSTDIVEN		BIT(11)
+#define DPTX_ANA_MPLL_POSTDIV		GENMASK(10, 8)
+
+/* PREPLL is the pixel PLL exposed as a clock. */
+#define DPTX_ANA_PREPLL			0x0190
+#define DPTX_ANA_PREPLL_FBDIV2_LBIT	GENMASK(31, 24)
+#define DPTX_ANA_PREPLL_FBDIV2_HBIT	GENMASK(19, 16)
+#define DPTX_ANA_PREPLL_PREDIV		GENMASK(13, 8)
+#define DPTX_ANA_PREPLL_LOCKED		BIT(7)
+#define DPTX_ANA_PREPLL_DACPD		BIT(5)
+#define DPTX_ANA_PREPLL_DSMPD		BIT(4)
+#define DPTX_ANA_PREPLL_PCLK_NORMAL	BIT(1)
+#define DPTX_ANA_PREPLL_PD		BIT(0)
+
+#define DPTX_ANA_PREPLL_DIV		0x0194
+#define DPTX_ANA_PREPLL_PRECLK_DIVM	GENMASK(17, 16)
+#define DPTX_ANA_PREPLL_PRECLK_DIVAUX	GENMASK(12, 8)
+
+#define DPTX_ANA_PREPLL_CTRL		0x0198
+#define DPTX_ANA_PREPLL_PCLKDIV5_EN	BIT(31)
+#define DPTX_ANA_PREPLL_PCLK_DIVAUX	GENMASK(28, 24)
+#define DPTX_ANA_PREPLL_LOCK_BYPEN	BIT(17)
+#define DPTX_ANA_PREPLL_HDMI_EN		BIT(9)
+#define DPTX_ANA_PREPLL_DP_EN		BIT(8)
+
+#define DPTX_ANA_PREPLL_FRAC		0x019c
+#define DPTX_ANA_PREPLL_FRAC2_LBIT	GENMASK(23, 16)
+#define DPTX_ANA_PREPLL_FRAC2_MBIT	GENMASK(15, 8)
+#define DPTX_ANA_PREPLL_FRAC2_HBIT	GENMASK(7, 0)
+
+#define DPTX_ANA_TX_CTRL		0x01a0
+#define DPTX_ANA_MPLL_CLKDIV_16M	GENMASK(13, 8)
+
+/* PRECLK_DIVM register value -> divide factor */
+static const u8 k3_inno_dp_divm_factors[] = { 1, 2, 3, 5 };
+
+#define DPTX_VCO_MIN_KHZ		1000000
+#define DPTX_VCO_MAX_KHZ		3000000
+#define DPTX_PLL_FRAC_MOD		(1 << 24)
+#define DPTX_PLL_ERR_TOLERANCE		10
+
+struct k3_inno_dp_phy {
+	struct device	*dev;
+	struct regmap	*regmap;
+	struct phy	*phy;
+	u8		lanes;		/* tracks last set_lanes */
+
+	struct clk_hw	pxclk_hw;	/* the PREPLL's pixel clock */
+	unsigned long	pxclk_rate;	/* last programmed PREPLL rate */
+	u32		ref_clk_khz;	/* PLL reference */
+};
+
+static int k3_inno_dp_phy_power_on(struct phy *phy)
+{
+	struct k3_inno_dp_phy *p = phy_get_drvdata(phy);
+	u32 lane_en;
+	u32 val;
+	int ret;
+
+	switch (p->lanes) {
+	case 1:
+		lane_en = 0x1;
+		break;
+	case 2:
+		lane_en = 0x3;
+		break;
+	case 4:
+	default:
+		lane_en = 0xf;
+		break;
+	}
+
+	ret = regmap_write_bits(p->regmap, DPTX_ANA_MPLL, DPTX_ANA_MPLL_PD, 0);
+	if (ret)
+		return ret;
+	ret = regmap_write_bits(p->regmap, DPTX_ANA_PREPLL,
+				DPTX_ANA_PREPLL_PD, 0);
+	if (ret)
+		return ret;
+	usleep_range(2000, 4000);
+
+	ret = regmap_write_bits(p->regmap, DPTX_PHY_CTRL,
+				DPTX_PHY_CTRL_XMIT_EN,
+				FIELD_PREP(DPTX_PHY_CTRL_XMIT_EN, lane_en));
+	if (ret)
+		return ret;
+	usleep_range(2000, 4000);
+
+	ret = regmap_read_poll_timeout(p->regmap, DPTX_ANA_MPLL, val,
+				       val & DPTX_ANA_MPLL_LOCKED,
+				       2000, 10 * 1000);
+	if (ret) {
+		dev_err(p->dev, "DP PHY core PLL lock timed out\n");
+		return ret;
+	}
+
+	ret = regmap_read_poll_timeout(p->regmap, DPTX_ANA_PREPLL, val,
+				       val & DPTX_ANA_PREPLL_LOCKED,
+				       2000, 10 * 1000);
+	if (ret)
+		dev_err(p->dev, "DP PHY pixel PLL lock timed out\n");
+
+	return ret;
+}
+
+static int k3_inno_dp_phy_power_off(struct phy *phy)
+{
+	struct k3_inno_dp_phy *p = phy_get_drvdata(phy);
+
+	regmap_write_bits(p->regmap, DPTX_PHY_CTRL, DPTX_PHY_CTRL_XMIT_EN, 0);
+	usleep_range(2000, 4000);
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PD,
+			  DPTX_ANA_PREPLL_PD);
+	usleep_range(2000, 4000);
+
+	return 0;
+}
+
+static int k3_inno_dp_phy_set_lanes(struct k3_inno_dp_phy *p, u8 lanes)
+{
+	u32 val;
+	int ret;
+
+	switch (lanes) {
+	case 1:
+		val = 0;
+		break;
+	case 2:
+		val = 1;
+		break;
+	case 4:
+		val = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write_bits(p->regmap, DPTX_PHY_CTRL,
+				DPTX_PHY_CTRL_NUM_LANES,
+				FIELD_PREP(DPTX_PHY_CTRL_NUM_LANES, val));
+	if (ret)
+		return ret;
+
+	p->lanes = lanes;
+	return 0;
+}
+
+struct k3_inno_dp_mpll_cfg {
+	unsigned int link_rate;		/* Mbps per lane */
+	u8 prediv;
+	u16 fbdiv;
+	u32 frac;
+	u8 postdiv;
+	u8 postdiv_en;
+	u8 frac_pd;
+	u8 vcoclk_div8_en;
+	u8 clk_16mdiv;
+};
+
+static const struct k3_inno_dp_mpll_cfg k3_inno_dp_mpll_cfgs[] = {
+	{
+		.link_rate	= 1620,
+		.prediv		= 0x02,
+		.fbdiv		= 0x87,
+		.frac		= 0,
+		.postdiv	= 0,
+		.postdiv_en	= 1,
+		.frac_pd	= 0x3,
+		.vcoclk_div8_en	= 1,
+		.clk_16mdiv	= 12,
+	}, {
+		.link_rate	= 2700,
+		.prediv		= 0x02,
+		.fbdiv		= 0xe1,
+		.frac		= 0,
+		.postdiv	= 0,
+		.postdiv_en	= 1,
+		.frac_pd	= 0x3,
+		.vcoclk_div8_en	= 1,
+		.clk_16mdiv	= 21,
+	}, {
+		.link_rate	= 5400,
+		.prediv		= 0x02,
+		.fbdiv		= 0xe1,
+		.frac		= 0,
+		.postdiv	= 0,
+		.postdiv_en	= 0,
+		.frac_pd	= 0x3,
+		.vcoclk_div8_en	= 0,
+		.clk_16mdiv	= 42,
+	},
+};
+
+static void k3_inno_dp_phy_program_mpll(struct k3_inno_dp_phy *p,
+					const struct k3_inno_dp_mpll_cfg *cfg)
+{
+	struct regmap *rm = p->regmap;
+
+	/*
+	 * Shared with an unwired HDMI path: select DP first, otherwise the
+	 * PLLs still lock but nothing reaches the lanes and AUX times out.
+	 */
+	regmap_write_bits(rm, DPTX_ANA_PREPLL_CTRL, DPTX_ANA_PREPLL_DP_EN,
+		DPTX_ANA_PREPLL_DP_EN);
+	regmap_write_bits(rm, DPTX_ANA_PREPLL_CTRL, DPTX_ANA_PREPLL_HDMI_EN, 0);
+
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_PD,
+		FIELD_PREP(DPTX_ANA_MPLL_PD, 1));
+	usleep_range(2000, 4000);
+
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_PREDIV,
+		FIELD_PREP(DPTX_ANA_MPLL_PREDIV, cfg->prediv));
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_FBDIV_LBIT,
+		FIELD_PREP(DPTX_ANA_MPLL_FBDIV_LBIT, cfg->fbdiv & 0xff));
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_FBDIV_HBIT,
+		FIELD_PREP(DPTX_ANA_MPLL_FBDIV_HBIT, (cfg->fbdiv >> 8) & 0xf));
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_DACPD,
+		FIELD_PREP(DPTX_ANA_MPLL_DACPD, (cfg->frac_pd >> 1) & 0x1));
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_DSMPD,
+		FIELD_PREP(DPTX_ANA_MPLL_DSMPD, cfg->frac_pd & 0x1));
+
+	regmap_write_bits(rm, DPTX_ANA_MPLL_FRAC, DPTX_ANA_MPLL_FRAC_LBIT,
+		FIELD_PREP(DPTX_ANA_MPLL_FRAC_LBIT, cfg->frac & 0xff));
+	regmap_write_bits(rm, DPTX_ANA_MPLL_FRAC, DPTX_ANA_MPLL_FRAC_MBIT,
+		FIELD_PREP(DPTX_ANA_MPLL_FRAC_MBIT, (cfg->frac >> 8) & 0xff));
+	regmap_write_bits(rm, DPTX_ANA_MPLL_FRAC, DPTX_ANA_MPLL_FRAC_HBIT,
+		FIELD_PREP(DPTX_ANA_MPLL_FRAC_HBIT, (cfg->frac >> 16) & 0xff));
+
+	regmap_write_bits(rm, DPTX_ANA_MPLL_DIV, DPTX_ANA_MPLL_POSTDIV,
+		FIELD_PREP(DPTX_ANA_MPLL_POSTDIV, cfg->postdiv));
+	regmap_write_bits(rm, DPTX_ANA_MPLL_DIV, DPTX_ANA_MPLL_POSTDIVEN,
+		FIELD_PREP(DPTX_ANA_MPLL_POSTDIVEN, cfg->postdiv_en));
+	regmap_write_bits(rm, DPTX_ANA_MPLL_DIV, DPTX_ANA_MPLL_VCOCLK_DIV8_EN,
+		FIELD_PREP(DPTX_ANA_MPLL_VCOCLK_DIV8_EN, cfg->vcoclk_div8_en));
+
+	regmap_write_bits(rm, DPTX_ANA_TX_CTRL, DPTX_ANA_MPLL_CLKDIV_16M,
+		FIELD_PREP(DPTX_ANA_MPLL_CLKDIV_16M, cfg->clk_16mdiv));
+
+	regmap_write_bits(rm, DPTX_ANA_PREPLL_CTRL, DPTX_ANA_PREPLL_LOCK_BYPEN,
+		FIELD_PREP(DPTX_ANA_PREPLL_LOCK_BYPEN, 1));
+
+	regmap_write_bits(rm, DPTX_ANA_MPLL, DPTX_ANA_MPLL_PD,
+		FIELD_PREP(DPTX_ANA_MPLL_PD, 0));
+	usleep_range(2000, 4000);
+}
+
+static int k3_inno_dp_phy_set_rate(struct k3_inno_dp_phy *p,
+				   unsigned int link_rate)
+{
+	const struct k3_inno_dp_mpll_cfg *cfg = NULL;
+	unsigned int i;
+	u32 sel;
+
+	switch (link_rate) {
+	case 1620:
+		sel = 0;
+		break;
+	case 2700:
+		sel = 1;
+		break;
+	case 5400:
+		sel = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(k3_inno_dp_mpll_cfgs); i++) {
+		if (k3_inno_dp_mpll_cfgs[i].link_rate == link_rate) {
+			cfg = &k3_inno_dp_mpll_cfgs[i];
+			break;
+		}
+	}
+	if (!cfg)
+		return -EINVAL;
+
+	k3_inno_dp_phy_program_mpll(p, cfg);
+
+	return regmap_write_bits(p->regmap, DPTX_PHY_CTRL,
+				 DPTX_PHY_CTRL_RATE,
+				 FIELD_PREP(DPTX_PHY_CTRL_RATE, sel));
+}
+
+static int k3_inno_dp_phy_set_voltages(struct k3_inno_dp_phy *p,
+				       struct phy_configure_opts_dp *opts)
+{
+	u8 lane;
+	int ret;
+
+	for (lane = 0; lane < opts->lanes; lane++) {
+		unsigned int shift = lane * DPTX_PHY_LANE_BITS_PER;
+		u32 mask = DPTX_PHY_LANE_FIELD_MASK << shift;
+		u32 val  = (((opts->voltage[lane] & 0x3) << DPTX_PHY_LANE_VSWING_SHIFT) |
+			    (opts->pre[lane] & 0x3)) << shift;
+
+		ret = regmap_write_bits(p->regmap, DPTX_PHY_LANE_TRIM, mask,
+					val);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int k3_inno_dp_phy_configure(struct phy *phy,
+				    union phy_configure_opts *opts)
+{
+	struct k3_inno_dp_phy *p = phy_get_drvdata(phy);
+	int ret;
+
+	if (opts->dp.set_lanes) {
+		ret = k3_inno_dp_phy_set_lanes(p, opts->dp.lanes);
+		if (ret)
+			return ret;
+	}
+
+	if (opts->dp.set_rate) {
+		ret = k3_inno_dp_phy_set_rate(p, opts->dp.link_rate);
+		if (ret)
+			return ret;
+	}
+
+	if (opts->dp.set_voltages) {
+		ret = k3_inno_dp_phy_set_voltages(p, &opts->dp);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static u32 k3_inno_dp_div64(u64 *n, u32 base)
+{
+	return do_div(*n, base);
+}
+
+static int k3_inno_dp_better_cfg(bool new_valid, bool new_is_int, u8 new_pre, u32 new_vco,
+				 bool best_valid, bool best_is_int, u8 best_pre, u32 best_vco)
+{
+	if (!new_valid)
+		return 0;
+	if (!best_valid)
+		return 1;
+
+	if (new_is_int && !best_is_int)
+		return 1;
+	if (!new_is_int && best_is_int)
+		return 0;
+
+	if (new_pre < best_pre)
+		return 1;
+	if (new_pre > best_pre)
+		return 0;
+
+	if (new_vco > best_vco)
+		return 1;
+
+	return 0;
+}
+
+struct k3_inno_dp_prepll_cfg {
+	u32 target_pclk_khz;
+	u32 vco_freq_khz;
+	u8 prediv;
+	u16 fbdiv;
+	u32 frac_pd;
+	u32 frac;
+	u8 div5_en;
+	u8 divm;
+	u8 divaux;
+	u8 divp;
+	u32 actual_pclk_khz;
+	bool valid;
+};
+
+static const struct k3_inno_dp_prepll_cfg k3_inno_dp_prepll_cfgs[] = {
+	{ 614400, 2460000, 0x05, 512,  0x3, 0x0, 0x0, 0x1, 0x01, 0x1, 614400, true },
+	{ 594000, 2376000, 0x01, 99,   0x3, 0x0, 0x0, 0x1, 0x01, 0x1, 594000, true },
+	{ 551040, 2760000, 0x05, 574,  0x3, 0x0, 0x1, 0x0, 0x00, 0x0, 551040, true },
+	{ 533250, 2130000, 0x08, 711,  0x3, 0x0, 0x0, 0x1, 0x01, 0x1, 533250, true },
+	{ 443250, 1770000, 0x08, 591,  0x3, 0x0, 0x0, 0x1, 0x01, 0x1, 443250, true },
+	{ 375000, 3000000, 0x01, 125,  0x3, 0x0, 0x0, 0x0, 0x04, 0x1, 375000, true },
+	{ 372000, 2980000, 0x01, 124,  0x3, 0x0, 0x0, 0x0, 0x04, 0x1, 372000, true },
+	{ 348500, 2790000, 0x06, 697,  0x3, 0x0, 0x0, 0x0, 0x04, 0x1, 348500, true },
+	{ 307200, 1540000, 0x01, 64,   0x3, 0x0, 0x1, 0x0, 0x00, 0x0, 307200, true },
+	{ 297000, 2376000, 0x01, 99,   0x3, 0x0, 0x0, 0x0, 0x04, 0x1, 297000, true },
+	{ 280000, 1680000, 0x01, 70,   0x3, 0x0, 0x0, 0x2, 0x01, 0x1, 280000, true },
+	{ 277440, 2770000, 0x05, 578,  0x3, 0x0, 0x0, 0x3, 0x01, 0x1, 277440, true },
+	{ 245760, 2457600, 0x05, 512,  0x3, 0x0, 0x0, 0x3, 0x01, 0x1, 245760, true },
+	{ 241500, 1932000, 0x02, 161,  0x3, 0x0, 0x0, 0x0, 0x04, 0x1, 241500, true },
+	{ 236000, 2830000, 0x01, 118,  0x3, 0x0, 0x0, 0x0, 0x06, 0x1, 236000, true },
+	{ 204800, 2048000, 0x03, 256,  0x3, 0x0, 0x0, 0x3, 0x01, 0x1, 204800, true },
+	{ 193250, 2320000, 0x08, 773,  0x3, 0x0, 0x0, 0x0, 0x06, 0x1, 193250, true },
+	{ 189000, 1510000, 0x01,  63,  0x3, 0x0, 0x0, 0x0, 0x04, 0x1, 189000, true },
+	{ 187500, 3000000, 0x01, 125,  0x3, 0x0, 0x0, 0x0, 0x08, 0x1, 187500, true },
+	{ 162000, 2592000, 0x01, 108,  0x3, 0x0, 0x0, 0x0, 0x08, 0x1, 162000, true },
+	{ 156000, 2810000, 0x01, 117,  0x3, 0x0, 0x0, 0x0, 0x09, 0x1, 156000, true },
+	{ 150000, 3000000, 0x01, 125,  0x3, 0x0, 0x0, 0x0, 0x0a, 0x1, 150000, true },
+	{ 148500, 2376000, 0x01, 99,   0x3, 0x0, 0x0, 0x0, 0x08, 0x1, 148500, true },
+	{ 146000, 1750000, 0x01, 73,   0x3, 0x0, 0x0, 0x0, 0x06, 0x1, 146000, true },
+	{ 142860, 2860000, 0x14, 2381, 0x3, 0x0, 0x0, 0x0, 0x0a, 0x1, 142860, true },
+	{ 140000, 2520000, 0x01, 105,  0x3, 0x0, 0x0, 0x0, 0x09, 0x1, 140000, true },
+	{ 138500, 2220000, 0x03, 277,  0x3, 0x0, 0x0, 0x0, 0x08, 0x1, 138500, true },
+	{ 122000, 2930000, 0x01, 122,  0x3, 0x0, 0x0, 0x0, 0x0c, 0x1, 122000, true },
+	{ 121750, 2920000, 0x04, 487,  0x3, 0x0, 0x0, 0x0, 0x0c, 0x1, 121750, true },
+	{ 108000, 2810000, 0x01, 117,  0x3, 0x0, 0x0, 0x0, 0x0d, 0x1, 108000, true },
+	{ 106500, 1700000, 0x01,  71,  0x3, 0x0, 0x0, 0x0, 0x08, 0x1, 106500, true },
+	{ 83500,  2000000, 0x02, 167,  0x3, 0x0, 0x0, 0x0, 0x0c, 0x1, 83500,  true },
+	{ 79500,  2540000, 0x01, 106,  0x3, 0x0, 0x0, 0x0, 0x10, 0x1, 79500,  true },
+	{ 75000,  3000000, 0x01, 125,  0x3, 0x0, 0x0, 0x0, 0x14, 0x1, 75000,  true },
+	{ 74250,  2376000, 0x01,  99,  0x3, 0x0, 0x0, 0x0, 0x10, 0x1, 74250,  true },
+	{ 65000,  1560000, 0x01,  65,  0x3, 0x0, 0x0, 0x0, 0x0c, 0x1, 65000,  true },
+	{ 40000,  2880000, 0x01, 120,  0x3, 0x0, 0x0, 0x0, 0x12, 0x2, 40000,  true },
+	{ 27000,  2810000, 0x01, 117,  0x3, 0x0, 0x0, 0x0, 0x1a, 0x2, 27000,  true },
+	{ 25600,  2300000, 0x01,  96,  0x3, 0x0, 0x0, 0x0, 0x0f, 0x3, 25600,  true },
+	{ 25200,  2520000, 0x01, 105,  0x3, 0x0, 0x0, 0x0, 0x19, 0x2, 25200,  true },
+};
+
+static u64 k3_inno_dp_abs_diff(u64 a, u64 b)
+{
+	return (a > b) ? (a - b) : (b - a);
+}
+
+static u32 k3_inno_dp_pll_rate_khz(u8 pre, u16 fb, u32 frac,
+				   u32 ref_clk_khz, u32 total_div)
+{
+	u64 vco_hz;
+	u64 ref_hz = (u64)ref_clk_khz * 1000;
+	u64 int_part;
+	u64 frac_part;
+
+	/* Integer part: (Ref * FB) / Pre */
+	int_part = ref_hz * fb;
+	int_part += (pre / 2);
+	k3_inno_dp_div64(&int_part, pre);
+
+	/* Fractional part: (Ref * Frac) / (Pre * 2^24) */
+	frac_part = ref_hz * frac;
+
+	frac_part += (pre / 2);
+	k3_inno_dp_div64(&frac_part, pre);
+
+	frac_part += (DPTX_PLL_FRAC_MOD / 2);
+	k3_inno_dp_div64(&frac_part, DPTX_PLL_FRAC_MOD);
+
+	vco_hz = int_part + frac_part;
+
+	/* Final Rate = VCO / total_div */
+	vco_hz += (total_div / 2);
+	k3_inno_dp_div64(&vco_hz, total_div);
+
+	vco_hz += 500;
+	k3_inno_dp_div64(&vco_hz, 1000);
+
+	return (u32)vco_hz;
+}
+
+static int k3_inno_dp_prepll_div_total(const struct k3_inno_dp_prepll_cfg *cfg,
+				       u32 *div_total)
+{
+	if (!cfg || !div_total || !cfg->valid)
+		return -EINVAL;
+
+	/* div5_en fixes div_total at 5; divp is unused on this path */
+	if (cfg->div5_en) {
+		*div_total = 5;
+		return 0;
+	}
+
+	if (!cfg->divp)
+		return -EINVAL;
+
+	if (cfg->divaux == 1) {
+		if (cfg->divm >= ARRAY_SIZE(k3_inno_dp_divm_factors))
+			return -EINVAL;
+
+		*div_total = 2 * k3_inno_dp_divm_factors[cfg->divm] * cfg->divp;
+		return 0;
+	}
+
+	if (cfg->divaux < 2)
+		return -EINVAL;
+
+	*div_total = 2 * cfg->divaux * cfg->divp;
+	return 0;
+}
+
+static bool k3_inno_dp_prepll_cfg_matches(const struct k3_inno_dp_prepll_cfg *cfg,
+					  u32 target_pclk_khz,
+					 u32 ref_clk_khz)
+{
+	u32 div_total;
+	u32 actual_pclk_khz;
+
+	if (k3_inno_dp_prepll_div_total(cfg, &div_total))
+		return false;
+
+	actual_pclk_khz = k3_inno_dp_pll_rate_khz(cfg->prediv, cfg->fbdiv,
+						  cfg->frac, ref_clk_khz,
+						  div_total);
+
+	if (cfg->actual_pclk_khz &&
+	    k3_inno_dp_abs_diff(actual_pclk_khz,
+				cfg->actual_pclk_khz) > DPTX_PLL_ERR_TOLERANCE)
+		return false;
+
+	return k3_inno_dp_abs_diff(actual_pclk_khz,
+				    target_pclk_khz) <= DPTX_PLL_ERR_TOLERANCE;
+}
+
+static const struct k3_inno_dp_prepll_cfg *k3_inno_dp_find_prepll_cfg(u32 pclk_khz,
+								      u32 ref_clk_khz)
+{
+	const struct k3_inno_dp_prepll_cfg *best_match = NULL;
+	u32 min_diff = 0xFFFFFFFF;
+	int num_configs = ARRAY_SIZE(k3_inno_dp_prepll_cfgs);
+
+	for (int i = 0; i < num_configs; i++) {
+		u32 current_target = k3_inno_dp_prepll_cfgs[i].target_pclk_khz;
+		u32 diff = k3_inno_dp_abs_diff(pclk_khz, current_target);
+
+		if (diff == 0 && k3_inno_dp_prepll_cfg_matches(&k3_inno_dp_prepll_cfgs[i],
+							       pclk_khz, ref_clk_khz)) {
+			return &k3_inno_dp_prepll_cfgs[i];
+		}
+
+		if ((pclk_khz / 100) == (current_target / 100) &&
+		    k3_inno_dp_prepll_cfg_matches(&k3_inno_dp_prepll_cfgs[i],
+						  pclk_khz, ref_clk_khz)) {
+			return &k3_inno_dp_prepll_cfgs[i];
+		}
+
+		if (diff < min_diff && diff < 500 &&
+		    k3_inno_dp_prepll_cfg_matches(&k3_inno_dp_prepll_cfgs[i],
+						  pclk_khz, ref_clk_khz)) {
+			min_diff = diff;
+			best_match = &k3_inno_dp_prepll_cfgs[i];
+		}
+	}
+
+	return best_match;
+}
+
+static int k3_inno_dp_solve_frac(u32 target_vco_khz, u32 ref_clk_khz,
+				 u8 *best_pre, u16 *best_fb, u32 *best_frac)
+{
+	u64 min_err = ~0ULL;
+	int found = 0;
+	bool best_is_int = false;
+	int pre;
+
+	for (pre = 1; pre <= 63; pre++) {
+		u64 ref_clk_hz = (u64)ref_clk_khz * 1000;
+		u64 target_vco_hz = (u64)target_vco_khz * 1000;
+
+		/*
+		 * Calculate required multiplier: Mult = (TargetVCO * Pre) / Ref
+		 */
+		u64 num = target_vco_hz * pre;
+		u64 den = ref_clk_hz;
+		u64 remainder;
+		u64 fb_val;
+		u64 frac_val;
+		u64 actual_vco;
+		u64 diff;
+		bool current_is_int;
+
+		fb_val = num;
+		remainder = k3_inno_dp_div64(&fb_val, (u32)den);
+
+		if (fb_val > 4095)
+			continue;
+
+		/* Frac = (Remainder * 2^24 + Ref/2) / Ref */
+		frac_val = remainder * DPTX_PLL_FRAC_MOD;
+		frac_val += (den / 2);
+		k3_inno_dp_div64(&frac_val, (u32)den);
+
+		if (frac_val > 0xFFFFFF)
+			frac_val = 0xFFFFFF;
+
+		/* VCO = (Ref * FB / Pre) + (Ref * Frac / (Pre * 2^24)) */
+		{
+			u64 vco_int, vco_frac;
+
+			vco_int = ref_clk_hz * fb_val;
+			k3_inno_dp_div64(&vco_int, pre);
+			vco_frac = ref_clk_hz * frac_val;
+			k3_inno_dp_div64(&vco_frac, pre);
+			k3_inno_dp_div64(&vco_frac, DPTX_PLL_FRAC_MOD);
+
+			actual_vco = vco_int + vco_frac;
+		}
+
+		diff = k3_inno_dp_abs_diff(actual_vco, target_vco_hz);
+		current_is_int = (frac_val == 0);
+
+		if (diff < min_err) {
+			min_err = diff;
+			*best_pre = pre;
+			*best_fb = (u16)fb_val;
+			*best_frac = (u32)frac_val;
+			best_is_int = current_is_int;
+			found = 1;
+		} else if (diff == min_err) {
+			if (current_is_int && !best_is_int) {
+				*best_pre = pre;
+				*best_fb = (u16)fb_val;
+				*best_frac = (u32)frac_val;
+				best_is_int = true;
+				found = 1;
+			}
+		}
+	}
+
+	return found ? 0 : -EINVAL;
+}
+
+/* divaux must be 1 for the DivM path and > 1 for the DivAux path. */
+static void k3_inno_dp_try_prepll(u32 target_pclk_khz, u32 ref_clk_khz,
+				  u32 div_total, u8 div5_en, u8 divaux,
+				  u8 divm, u8 divp,
+				  struct k3_inno_dp_prepll_cfg *best)
+{
+	struct k3_inno_dp_prepll_cfg curr = {0};
+	u32 target_vco = target_pclk_khz * div_total;
+	u32 actual_pclk;
+	u32 frac;
+	u16 fb;
+	u8 pre;
+
+	if (target_vco < DPTX_VCO_MIN_KHZ || target_vco > DPTX_VCO_MAX_KHZ)
+		return;
+
+	if (k3_inno_dp_solve_frac(target_vco, ref_clk_khz, &pre, &fb, &frac))
+		return;
+
+	actual_pclk = k3_inno_dp_pll_rate_khz(pre, fb, frac, ref_clk_khz,
+					      div_total);
+	if (k3_inno_dp_abs_diff(actual_pclk, target_pclk_khz) > DPTX_PLL_ERR_TOLERANCE)
+		return;
+
+	curr.valid = true;
+	curr.vco_freq_khz = target_vco;
+	curr.actual_pclk_khz = actual_pclk;
+	curr.prediv = pre;
+	curr.fbdiv = fb;
+	curr.frac = frac;
+	curr.frac_pd = (frac == 0) ? 3 : 0;
+	curr.div5_en = div5_en;
+	curr.divaux = divaux;
+	curr.divm = divm;
+	curr.divp = divp;
+
+	if (k3_inno_dp_better_cfg(curr.valid, curr.frac == 0, curr.prediv,
+				  curr.vco_freq_khz, best->valid,
+				    best->frac == 0, best->prediv,
+				    best->vco_freq_khz))
+		*best = curr;
+}
+
+static int k3_inno_dp_solve_prepll(u32 target_pclk_khz, u32 ref_clk_khz,
+				   struct k3_inno_dp_prepll_cfg *cfg)
+{
+	struct k3_inno_dp_prepll_cfg best = {0};
+	int pclk_div;
+	int i;
+
+	/* Strategy 1: Div5 path (VCO = PCLK * 5) */
+	k3_inno_dp_try_prepll(target_pclk_khz, ref_clk_khz, 5, 1, 0, 0, 0,
+			      &best);
+
+	for (pclk_div = 1; pclk_div <= 31; pclk_div++) {
+		/* Strategy 2: DivM path */
+		for (i = 0; i < ARRAY_SIZE(k3_inno_dp_divm_factors); i++)
+			k3_inno_dp_try_prepll(target_pclk_khz, ref_clk_khz,
+				2 * k3_inno_dp_divm_factors[i] * pclk_div, 0, 1,
+				i, pclk_div, &best);
+
+		/* Strategy 3: DivAux path */
+		for (i = 2; i <= 31; i++)
+			k3_inno_dp_try_prepll(target_pclk_khz, ref_clk_khz,
+					      2 * i * pclk_div,
+					      0, i, 0, pclk_div, &best);
+	}
+
+	if (!best.valid)
+		return -EINVAL;
+
+	*cfg = best;
+	return 0;
+}
+
+static void k3_inno_dp_program_prepll(struct k3_inno_dp_phy *p,
+				      const struct k3_inno_dp_prepll_cfg *cfg)
+{
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PD,
+		FIELD_PREP(DPTX_ANA_PREPLL_PD, 1));
+	usleep_range(2000, 4000);
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PREDIV,
+		FIELD_PREP(DPTX_ANA_PREPLL_PREDIV, cfg->prediv));
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL,
+		DPTX_ANA_PREPLL_FBDIV2_LBIT,
+		FIELD_PREP(DPTX_ANA_PREPLL_FBDIV2_LBIT, cfg->fbdiv & 0xFF));
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL,
+		DPTX_ANA_PREPLL_FBDIV2_HBIT,
+		FIELD_PREP(DPTX_ANA_PREPLL_FBDIV2_HBIT, (cfg->fbdiv >> 8) & 0xF));
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_DACPD,
+		FIELD_PREP(DPTX_ANA_PREPLL_DACPD, (cfg->frac_pd >> 1) & 0x1));
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_DSMPD,
+		FIELD_PREP(DPTX_ANA_PREPLL_DSMPD, cfg->frac_pd & 0x1));
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_FRAC,
+		DPTX_ANA_PREPLL_FRAC2_LBIT,
+		FIELD_PREP(DPTX_ANA_PREPLL_FRAC2_LBIT, cfg->frac & 0xFF));
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_FRAC,
+		DPTX_ANA_PREPLL_FRAC2_MBIT,
+		FIELD_PREP(DPTX_ANA_PREPLL_FRAC2_MBIT, (cfg->frac >> 8) & 0xFF));
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_FRAC,
+		DPTX_ANA_PREPLL_FRAC2_HBIT,
+		FIELD_PREP(DPTX_ANA_PREPLL_FRAC2_HBIT, (cfg->frac >> 16) & 0xFF));
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_DIV,
+		DPTX_ANA_PREPLL_PRECLK_DIVM,
+		FIELD_PREP(DPTX_ANA_PREPLL_PRECLK_DIVM, cfg->divm));
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_DIV,
+		DPTX_ANA_PREPLL_PRECLK_DIVAUX,
+		FIELD_PREP(DPTX_ANA_PREPLL_PRECLK_DIVAUX, cfg->divaux));
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_CTRL,
+		DPTX_ANA_PREPLL_PCLKDIV5_EN,
+		FIELD_PREP(DPTX_ANA_PREPLL_PCLKDIV5_EN, cfg->div5_en));
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL_CTRL,
+		DPTX_ANA_PREPLL_PCLK_DIVAUX,
+		FIELD_PREP(DPTX_ANA_PREPLL_PCLK_DIVAUX, cfg->divp));
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL,
+		DPTX_ANA_PREPLL_PCLK_NORMAL,
+		FIELD_PREP(DPTX_ANA_PREPLL_PCLK_NORMAL, 1));
+	usleep_range(2000, 4000);
+
+	regmap_write_bits(p->regmap, DPTX_ANA_PREPLL, DPTX_ANA_PREPLL_PD,
+		FIELD_PREP(DPTX_ANA_PREPLL_PD, 0));
+	usleep_range(2000, 4000);
+}
+
+#define to_k3_inno_dp_phy_clk(_hw) \
+	container_of(_hw, struct k3_inno_dp_phy, pxclk_hw)
+
+static unsigned long k3_inno_dp_pxclk_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	struct k3_inno_dp_phy *p = to_k3_inno_dp_phy_clk(hw);
+
+	return p->pxclk_rate;
+}
+
+static int k3_inno_dp_pxclk_determine_rate(struct clk_hw *hw,
+					   struct clk_rate_request *req)
+{
+	struct k3_inno_dp_phy *p = to_k3_inno_dp_phy_clk(hw);
+	const struct k3_inno_dp_prepll_cfg *cfg;
+	struct k3_inno_dp_prepll_cfg solved;
+	u32 khz = div_u64(req->rate, 1000);
+	int ret;
+
+	cfg = k3_inno_dp_find_prepll_cfg(khz, p->ref_clk_khz);
+	if (cfg) {
+		req->rate = (unsigned long)cfg->actual_pclk_khz * 1000;
+		return 0;
+	}
+
+	ret = k3_inno_dp_solve_prepll(khz, p->ref_clk_khz, &solved);
+	if (ret)
+		return ret;
+
+	req->rate = (unsigned long)solved.actual_pclk_khz * 1000;
+
+	return 0;
+}
+
+static int k3_inno_dp_pxclk_set_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long parent_rate)
+{
+	struct k3_inno_dp_phy *p = to_k3_inno_dp_phy_clk(hw);
+	const struct k3_inno_dp_prepll_cfg *cfg;
+	struct k3_inno_dp_prepll_cfg solved;
+	u32 khz = div_u64(rate, 1000);
+	int ret;
+
+	cfg = k3_inno_dp_find_prepll_cfg(khz, p->ref_clk_khz);
+	if (!cfg) {
+		ret = k3_inno_dp_solve_prepll(khz, p->ref_clk_khz, &solved);
+		if (ret) {
+			dev_err(p->dev, "no PLL setting for %lu Hz\n", rate);
+			return ret;
+		}
+		cfg = &solved;
+	}
+
+	k3_inno_dp_program_prepll(p, cfg);
+	p->pxclk_rate = (unsigned long)cfg->actual_pclk_khz * 1000;
+
+	return 0;
+}
+
+static const struct clk_ops k3_inno_dp_pxclk_ops = {
+	.recalc_rate	= k3_inno_dp_pxclk_recalc_rate,
+	.determine_rate	= k3_inno_dp_pxclk_determine_rate,
+	.set_rate	= k3_inno_dp_pxclk_set_rate,
+};
+
+static const struct phy_ops k3_inno_dp_phy_ops = {
+	.power_on	= k3_inno_dp_phy_power_on,
+	.power_off	= k3_inno_dp_phy_power_off,
+	.configure	= k3_inno_dp_phy_configure,
+	.owner		= THIS_MODULE,
+};
+
+static int k3_inno_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct k3_inno_dp_phy *p;
+	struct phy_provider *provider;
+	struct clk_init_data init = {};
+	int ret;
+
+	p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	p->dev = dev;
+
+	p->regmap = dev_get_regmap(dev->parent, "dp");
+	if (!p->regmap)
+		return dev_err_probe(dev, -ENODEV,
+				     "parent DP regmap not available\n");
+
+	p->phy = devm_phy_create(dev, dev->of_node, &k3_inno_dp_phy_ops);
+	if (IS_ERR(p->phy))
+		return dev_err_probe(dev, PTR_ERR(p->phy),
+				     "failed to create DP PHY\n");
+
+	phy_set_drvdata(p->phy, p);
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(provider))
+		return dev_err_probe(dev, PTR_ERR(provider),
+				     "failed to register PHY provider\n");
+
+	/* PHY takes a 24 MHz reference clock */
+	p->ref_clk_khz = 24000;
+
+	/* The APMU pixel-clock mux references this clock by name. */
+	of_property_read_string(dev->of_node, "clock-output-names", &init.name);
+	if (!init.name)
+		init.name = dev_name(dev);
+	init.ops = &k3_inno_dp_pxclk_ops;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+	init.flags = 0;
+	p->pxclk_hw.init = &init;
+
+	ret = devm_clk_hw_register(dev, &p->pxclk_hw);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to register pixel clock\n");
+
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+					  &p->pxclk_hw);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to add pixel clock provider\n");
+
+	return 0;
+}
+
+static const struct of_device_id k3_inno_dp_phy_of_match[] = {
+	{ .compatible = "spacemit,k3-inno-dp-phy" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, k3_inno_dp_phy_of_match);
+
+static struct platform_driver k3_inno_dp_phy_driver = {
+	.probe = k3_inno_dp_phy_probe,
+	.driver = {
+		.name		= "spacemit-k3-inno-dp-phy",
+		.of_match_table	= k3_inno_dp_phy_of_match,
+	},
+};
+module_platform_driver(k3_inno_dp_phy_driver);
+
+MODULE_DESCRIPTION("SpacemiT K3 Innosilicon DisplayPort PHY driver");
+MODULE_AUTHOR("Cody Kang <codykang.hk@gmail.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0



  parent reply	other threads:[~2026-07-25  4:51 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-25  4:51 [PATCH RESEND 00/17] drm/spacemit: add SpacemiT K3 display support Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 01/17] dt-bindings: display: spacemit: add K3 Saturn DPU controller Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 02/17] dt-bindings: phy: add SpacemiT K3 Innosilicon DP PHY Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 03/17] dt-bindings: display: spacemit: add K3 Innosilicon DP/eDP controller Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 04/17] dt-bindings: soc: spacemit: allow eDP/DP PHY PLL pixel clocks on K3 APMU Cody Kang via B4 Relay
2026-07-25  4:51 ` Cody Kang via B4 Relay [this message]
2026-07-25  5:02   ` [PATCH RESEND 05/17] phy: spacemit: add Innosilicon DP TX PHY driver sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 06/17] clk: spacemit: k3: parent eDP/DP pixel clock to the PHY PLL Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 07/17] drm/spacemit: add Saturn DPU register model Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU Cody Kang via B4 Relay
2026-07-25  5:04   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 09/17] drm/spacemit: add Saturn DPU hardware backend Cody Kang via B4 Relay
2026-07-25  5:12   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 10/17] drm/spacemit: add Saturn DPU KMS pipeline Cody Kang via B4 Relay
2026-07-25  5:04   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 11/17] drm/spacemit: add Saturn DPU DRM device driver Cody Kang via B4 Relay
2026-07-25  5:05   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 12/17] drm/spacemit: add Innosilicon DP/eDP controller bridge driver Cody Kang via B4 Relay
2026-07-25  5:04   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 13/17] MAINTAINERS: add SpacemiT K3 display driver entry Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 14/17] riscv: dts: spacemit: k3: add display nodes Cody Kang via B4 Relay
2026-07-25  5:08   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 15/17] riscv: dts: spacemit: k3-pico-itx: enable the DisplayPort output Cody Kang via B4 Relay
2026-07-25  5:12   ` sashiko-bot
2026-07-25  4:51 ` [PATCH RESEND 16/17] riscv: dts: spacemit: k3-com260-ifx: " Cody Kang via B4 Relay
2026-07-25  4:51 ` [PATCH RESEND 17/17] riscv: defconfig: spacemit: k3: enable display driver Cody Kang via B4 Relay

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260725-k3-display-v1-5-6de34d80e86c@gmail.com \
    --to=devnull+codykang.hk.gmail.com@kernel.org \
    --cc=airlied@gmail.com \
    --cc=alex@ghiti.fr \
    --cc=aou@eecs.berkeley.edu \
    --cc=bmasney@redhat.com \
    --cc=codykang.hk@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlan@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=heylenay@4d2.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-phy@lists.infradead.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=neil.armstrong@linaro.org \
    --cc=p.zabel@pengutronix.de \
    --cc=palmer@dabbelt.com \
    --cc=pjw@kernel.org \
    --cc=robh@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=spacemit@lists.linux.dev \
    --cc=tzimmermann@suse.de \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox