[PATCH RFC/DO NOT MERGE 09/12] drm/msm/hdmi: Add HDMI for Eliza SoC

Krzysztof Kozlowski krzysztof.kozlowski at oss.qualcomm.com
Fri Aug 28 07:05:07 PDT 2026


Qualcomm Eliza SoC with DPU v12.4 comes with HDMI on INTF_11 with
additional INTR registers.  The device clocks look different comparing
to last variant of HDMI from MSM8998.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski at oss.qualcomm.com>
---
 .../gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h |  8 +++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c  | 15 +++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.h  |  3 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h        |  3 +
 drivers/gpu/drm/msm/hdmi/hdmi.c                    | 32 +++++++++
 drivers/gpu/drm/msm/hdmi/hdmi.h                    |  4 ++
 drivers/gpu/drm/msm/hdmi/hdmi_bridge.c             | 75 +++++++++++++++++++---
 drivers/gpu/drm/msm/msm_drv.h                      |  5 ++
 8 files changed, 136 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h
index b93d32888972..c95cf4e38c12 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_4_eliza.h
@@ -294,6 +294,14 @@ static const struct dpu_intf_cfg eliza_intf[] = {
 		.prog_fetch_lines_worst_case = 24,
 		.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30),
 		.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31),
+	}, {
+		.name = "intf_11", .id = INTF_11,
+		.base = 0x3f000, .len = 0x4bc,
+		.type = INTF_HDMI,
+		.controller_id = MSM_HDMI_CONTROLLER_0,
+		.prog_fetch_lines_worst_case = 24,
+		.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 14),
+		.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 15),
 	}
 };
 
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c
index 5b7cd5241f45..f6d1f944f4ba 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c
@@ -206,6 +206,21 @@ static const struct dpu_intr_reg dpu_intr_set_7xxx[] = {
 		MDP_INTF_REV_7xxx_INTR_EN(8),
 		MDP_INTF_REV_7xxx_INTR_STATUS(8)
 	},
+	[MDP_INTF9_INTR] = {
+		MDP_INTF_REV_7xxx_INTR_CLEAR(9),
+		MDP_INTF_REV_7xxx_INTR_EN(9),
+		MDP_INTF_REV_7xxx_INTR_STATUS(9)
+	},
+	[MDP_INTF10_INTR] = {
+		MDP_INTF_REV_7xxx_INTR_CLEAR(10),
+		MDP_INTF_REV_7xxx_INTR_EN(10),
+		MDP_INTF_REV_7xxx_INTR_STATUS(10)
+	},
+	[MDP_INTF11_INTR] = {
+		MDP_INTF_REV_7xxx_INTR_CLEAR(11),
+		MDP_INTF_REV_7xxx_INTR_EN(11),
+		MDP_INTF_REV_7xxx_INTR_STATUS(11)
+	},
 };
 
 /*
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.h
index 142358a105c5..231809607be1 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.h
@@ -27,6 +27,9 @@ enum dpu_hw_intr_reg {
 	MDP_INTF6_INTR,
 	MDP_INTF7_INTR,
 	MDP_INTF8_INTR,
+	MDP_INTF9_INTR,
+	MDP_INTF10_INTR,
+	MDP_INTF11_INTR,
 	MDP_INTF1_TEAR_INTR,
 	MDP_INTF2_TEAR_INTR,
 	MDP_AD4_0_INTR,
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
index 0e65bf5ddc4a..bdcc0caa4e4f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
@@ -226,6 +226,9 @@ enum dpu_intf {
 	INTF_6,
 	INTF_7,
 	INTF_8,
+	INTF_9,
+	INTF_10,
+	INTF_11,
 	INTF_MAX
 };
 
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index c46f30063f14..b769780cd5dd 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -234,6 +234,16 @@ static const struct hdmi_platform_config hdmi_tx_8974_config = {
 	.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names_8x74),
 };
 
+static const char * const pwr_reg_names_eliza[] = {"core-vdda", "core-vcc", "vddio"};
+static const char * const pwr_clk_names_eliza[] = {"iface", "core"};
+
+static const struct hdmi_platform_config hdmi_tx_eliza_config = {
+	.pwr_reg_names = pwr_reg_names_eliza,
+	.pwr_reg_cnt = ARRAY_SIZE(pwr_reg_names_eliza),
+	.pwr_clk_names = pwr_clk_names_eliza,
+	.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names_eliza),
+};
+
 static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct msm_drm_private *priv = dev_get_drvdata(master);
@@ -360,6 +370,27 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
 		goto err_put_bridge;
 	}
 
+	hdmi->phy_iface_clk = devm_clk_get_optional(dev, "phy_iface");
+	if (IS_ERR(hdmi->phy_iface_clk)) {
+		ret = dev_err_probe(dev, PTR_ERR(hdmi->phy_iface_clk),
+				    "failed to get phy iface clock\n");
+		goto err_put_bridge;
+	}
+
+	hdmi->pixel_src_clk = devm_clk_get_optional(dev, "pixel_src");
+	if (IS_ERR(hdmi->pixel_src_clk)) {
+		ret = dev_err_probe(dev, PTR_ERR(hdmi->pixel_src_clk),
+				    "failed to get pixel src clock\n");
+		goto err_put_bridge;
+	}
+
+	hdmi->pll_clk = devm_clk_get_optional(dev, "pll");
+	if (IS_ERR(hdmi->pll_clk)) {
+		ret = dev_err_probe(dev, PTR_ERR(hdmi->pll_clk),
+				    "failed to get pll clock\n");
+		goto err_put_bridge;
+	}
+
 	hdmi->hpd_gpiod = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
 	/* This will catch e.g. -EPROBE_DEFER */
 	if (IS_ERR(hdmi->hpd_gpiod)) {
@@ -458,6 +489,7 @@ static int msm_hdmi_runtime_resume(struct device *dev)
 static DEFINE_RUNTIME_DEV_PM_OPS(msm_hdmi_pm_ops, msm_hdmi_runtime_suspend, msm_hdmi_runtime_resume, NULL);
 
 static const struct of_device_id msm_hdmi_dt_match[] = {
+	{ .compatible = "qcom,eliza-hdmi-tx", .data = &hdmi_tx_eliza_config },
 	{ .compatible = "qcom,hdmi-tx-8998", .data = &hdmi_tx_8974_config },
 	{ .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8974_config },
 	{ .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8974_config },
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index 306b988bca72..2fa9bec455b4 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -52,6 +52,9 @@ struct hdmi {
 	struct regulator_bulk_data *pwr_regs;
 	struct clk_bulk_data *pwr_clks;
 	struct clk *extp_clk;
+	struct clk *phy_iface_clk;
+	struct clk *pixel_src_clk;
+	struct clk *pll_clk;
 
 	struct gpio_desc *hpd_gpiod;
 
@@ -171,6 +174,7 @@ struct hdmi_phy {
 	struct platform_device *pdev;
 	void __iomem *mmio;
 	struct hdmi_phy_cfg *cfg;
+	void *pll;
 	const struct hdmi_phy_funcs *funcs;
 	struct regulator_bulk_data *regs;
 	struct clk **clks;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
index 621083a97b37..c6a75f653915 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
@@ -14,6 +14,18 @@
 #include "msm_kms.h"
 #include "hdmi.h"
 
+static void msm_hdmi_clk_reparent(struct hdmi *hdmi)
+{
+	int ret;
+
+	if (hdmi->pixel_src_clk && hdmi->pll_clk) {
+		ret = clk_set_parent(hdmi->pixel_src_clk, hdmi->pll_clk);
+		if (ret)
+			DRM_DEV_ERROR(hdmi->dev->dev, "failed to set reparent pixel src clock rate: %d\n",
+				      ret);
+	}
+}
+
 static void msm_hdmi_power_on(struct drm_bridge *bridge)
 {
 	struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
@@ -29,16 +41,34 @@ static void msm_hdmi_clk_prepare(struct drm_bridge *bridge)
 	struct hdmi *hdmi = hdmi_bridge->hdmi;
 	int ret;
 
+	msm_hdmi_clk_reparent(hdmi);
 	if (hdmi->extp_clk) {
+		/*
+		 * Set rate to hdmi->pixclock: PCLK_CLK_SRC and PCLK_CLK
+		 * Set rate to hdmi->pixclock/2: INTF_CLK and PCLK_DIV_CLK_SRC
+		 * Set rate to hdmi->pixclock: HDMI PHY PLL
+		 */
 		DBG("pixclock: %lu", hdmi->pixclock);
 
 		ret = dev_pm_opp_set_rate(&hdmi->pdev->dev, hdmi->pixclock);
 		if (ret)
 			DRM_DEV_ERROR(dev->dev, "failed to set OPP rate: %d\n", ret);
 
+		ret = clk_set_rate(hdmi->pixel_src_clk, hdmi->pixclock);
+		if (ret)
+			DRM_DEV_ERROR(dev->dev, "failed to set pixel src clk rate: %d\n", ret);
+
+		ret = clk_set_rate(hdmi->phy_iface_clk, hdmi->pixclock / 2);
+		if (ret)
+			DRM_DEV_ERROR(dev->dev, "failed to set pixel iface clk rate: %d\n", ret);
+
 		ret = clk_prepare_enable(hdmi->extp_clk);
 		if (ret)
 			DRM_DEV_ERROR(dev->dev, "failed to enable extp clk: %d\n", ret);
+
+		ret = clk_prepare_enable(hdmi->phy_iface_clk);
+		if (ret)
+			DRM_DEV_ERROR(dev->dev, "failed to enable phy iface clk: %d\n", ret);
 	}
 }
 
@@ -61,6 +91,7 @@ static void msm_hdmi_clk_unprepare(struct drm_bridge *bridge)
 	struct hdmi *hdmi = hdmi_bridge->hdmi;
 
 	if (hdmi->extp_clk) {
+		clk_disable_unprepare(hdmi->phy_iface_clk);
 		clk_disable_unprepare(hdmi->extp_clk);
 		dev_pm_opp_set_rate(&hdmi->pdev->dev, 0);
 	}
@@ -443,21 +474,47 @@ static enum drm_mode_status msm_hdmi_bridge_tmds_char_rate_valid(const struct dr
 	 * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder
 	 * instead):
 	 */
-	if (kms->funcs->round_pixclk)
+	if (kms->funcs->round_pixclk) {
 		actual = kms->funcs->round_pixclk(kms,
 						  tmds_rate,
 						  hdmi_bridge->hdmi->encoder);
-	else if (hdmi->extp_clk)
-		actual = clk_round_rate(hdmi->extp_clk, tmds_rate);
-	else
-		actual = tmds_rate;
+		DBG("requested=%lld, actual=%ld", tmds_rate, actual);
 
-	DBG("requested=%lld, actual=%ld", tmds_rate, actual);
+		if (actual != tmds_rate)
+			return MODE_CLOCK_RANGE;
+		return MODE_OK;
+	} else if (!dev_pm_opp_get_opp_count(&hdmi->pdev->dev)) {
+		if (hdmi->extp_clk)
+			actual = clk_round_rate(hdmi->extp_clk, tmds_rate);
+		else
+			actual = tmds_rate;
 
-	if (actual != tmds_rate)
-		return MODE_CLOCK_RANGE;
+		if (actual != tmds_rate)
+			return MODE_CLOCK_RANGE;
+		return MODE_OK;
+	} else {
+		struct dev_pm_opp *opp;
+		unsigned long pclk_rate = tmds_rate;
 
-	return 0;
+		if (mode->clock > 600000)
+			return MODE_CLOCK_HIGH;
+
+		opp = dev_pm_opp_find_freq_ceil(&hdmi->pdev->dev, &pclk_rate);
+		if (!IS_ERR(opp)) {
+			dev_pm_opp_put(opp);
+		} else if (PTR_ERR(opp) == -ERANGE) {
+			/*
+			 * An empty table is created by devm_pm_opp_set_clkname() even
+			 * if there is none. Thus find_freq_ceil will still return
+			 * -ERANGE in such case.
+			 */
+			return MODE_CLOCK_RANGE;
+		} else {
+			return MODE_ERROR;
+		}
+
+		return MODE_OK;
+	}
 }
 
 static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index eb4bbae8557b..e63c63ae7d6f 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -67,6 +67,11 @@ enum msm_dsi_controller {
 	MSM_DSI_CONTROLLER_COUNT,
 };
 
+enum msm_hdmi_controller {
+	MSM_HDMI_CONTROLLER_0,
+	MSM_HDMI_CONTROLLER_COUNT,
+};
+
 #define MSM_GPU_MAX_RINGS 4
 
 struct msm_drm_private {

-- 
2.53.0




More information about the linux-phy mailing list