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
next prev 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