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 <[email protected]> --- .../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
