[PATCH RFC/DO NOT MERGE 09/12] drm/msm/hdmi: Add HDMI for Eliza SoC
From: Krzysztof Kozlowski
Date: Fri Aug 28 2026 - 10:14:01 EST
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@xxxxxxxxxxxxxxxx>
---
.../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