[PATCH v7 4/6] drm/msm: add msm8998 hdmi phy/pll support
Dmitry Baryshkov
dmitry.baryshkov at linaro.org
Sat Aug 31 10:26:49 UTC 2024
On 24/07/2024 18:01, Marc Gonzalez wrote:
> From: Arnaud Vrac <avrac at freebox.fr>
>
> Add support for the HDMI PHY as present on the Qualcomm MSM8998 SoC.
> This code is mostly copy & paste of the vendor code from msm-4.4
> kernel.lnx.4.4.r38-rel.
>
> Signed-off-by: Arnaud Vrac <avrac at freebox.fr>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov at linaro.org>
> Signed-off-by: Marc Gonzalez <mgonzalez at freebox.fr>
> ---
> drivers/gpu/drm/msm/Makefile | 1 +
> drivers/gpu/drm/msm/hdmi/hdmi.h | 8 +
> drivers/gpu/drm/msm/hdmi/hdmi_phy.c | 5 +
> drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c | 779 +++++++++++++++++++++++++
> drivers/gpu/drm/msm/registers/display/hdmi.xml | 89 +++
> 5 files changed, 882 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index eb788921ff4fe..b9a5dc8c33ede 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -32,6 +32,7 @@ msm-display-$(CONFIG_DRM_MSM_HDMI) += \
> hdmi/hdmi_phy.o \
> hdmi/hdmi_phy_8960.o \
> hdmi/hdmi_phy_8996.o \
> + hdmi/hdmi_phy_8998.o \
> hdmi/hdmi_phy_8x60.o \
> hdmi/hdmi_phy_8x74.o \
> hdmi/hdmi_pll_8960.o \
> diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
> index 4586baf364151..a62d2aedfbb72 100644
> --- a/drivers/gpu/drm/msm/hdmi/hdmi.h
> +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
> @@ -137,6 +137,7 @@ enum hdmi_phy_type {
> MSM_HDMI_PHY_8960,
> MSM_HDMI_PHY_8x74,
> MSM_HDMI_PHY_8996,
> + MSM_HDMI_PHY_8998,
> MSM_HDMI_PHY_MAX,
> };
>
> @@ -154,6 +155,7 @@ extern const struct hdmi_phy_cfg msm_hdmi_phy_8x60_cfg;
> extern const struct hdmi_phy_cfg msm_hdmi_phy_8960_cfg;
> extern const struct hdmi_phy_cfg msm_hdmi_phy_8x74_cfg;
> extern const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg;
> +extern const struct hdmi_phy_cfg msm_hdmi_phy_8998_cfg;
>
> struct hdmi_phy {
> struct platform_device *pdev;
> @@ -184,6 +186,7 @@ void __exit msm_hdmi_phy_driver_unregister(void);
> #ifdef CONFIG_COMMON_CLK
> int msm_hdmi_pll_8960_init(struct platform_device *pdev);
> int msm_hdmi_pll_8996_init(struct platform_device *pdev);
> +int msm_hdmi_pll_8998_init(struct platform_device *pdev);
> #else
> static inline int msm_hdmi_pll_8960_init(struct platform_device *pdev)
> {
> @@ -194,6 +197,11 @@ static inline int msm_hdmi_pll_8996_init(struct platform_device *pdev)
> {
> return -ENODEV;
> }
> +
> +static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev)
> +{
> + return -ENODEV;
> +}
> #endif
>
> /*
> diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
> index 88a3423b7f24d..95b3f7535d840 100644
> --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
> +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
> @@ -118,6 +118,9 @@ static int msm_hdmi_phy_pll_init(struct platform_device *pdev,
> case MSM_HDMI_PHY_8996:
> ret = msm_hdmi_pll_8996_init(pdev);
> break;
> + case MSM_HDMI_PHY_8998:
> + ret = msm_hdmi_pll_8998_init(pdev);
> + break;
> /*
> * we don't have PLL support for these, don't report an error for now
> */
> @@ -193,6 +196,8 @@ static const struct of_device_id msm_hdmi_phy_dt_match[] = {
> .data = &msm_hdmi_phy_8x74_cfg },
> { .compatible = "qcom,hdmi-phy-8996",
> .data = &msm_hdmi_phy_8996_cfg },
> + { .compatible = "qcom,hdmi-phy-8998",
> + .data = &msm_hdmi_phy_8998_cfg },
> {}
> };
>
> diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c
> new file mode 100644
> index 0000000000000..9a3b005fa391a
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c
> @@ -0,0 +1,779 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2016, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2024 Freebox SAS
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +
> +#include "hdmi.h"
> +
> +#define HDMI_VCO_MAX_FREQ 12000000000UL
> +#define HDMI_VCO_MIN_FREQ 8000000000UL
> +
> +#define HDMI_PCLK_MAX_FREQ 600000000
> +#define HDMI_PCLK_MIN_FREQ 25000000
> +
> +#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL
> +#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL
> +#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL
> +#define HDMI_CORECLK_DIV 5
> +#define HDMI_DEFAULT_REF_CLOCK 19200000
> +#define HDMI_PLL_CMP_CNT 1024
> +
> +#define HDMI_PLL_POLL_MAX_READS 100
> +#define HDMI_PLL_POLL_TIMEOUT_US 150
> +
> +#define HDMI_NUM_TX_CHANNEL 4
> +
> +struct hdmi_pll_8998 {
> + struct platform_device *pdev;
> + struct clk_hw clk_hw;
> + unsigned long rate;
> +
> + /* pll mmio base */
> + void __iomem *mmio_qserdes_com;
> + /* tx channel base */
> + void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL];
> +};
> +
> +#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8998, clk_hw)
> +
> +struct hdmi_8998_phy_pll_reg_cfg {
> + u32 com_svs_mode_clk_sel;
> + u32 com_hsclk_sel;
> + u32 com_pll_cctrl_mode0;
> + u32 com_pll_rctrl_mode0;
> + u32 com_cp_ctrl_mode0;
> + u32 com_dec_start_mode0;
> + u32 com_div_frac_start1_mode0;
> + u32 com_div_frac_start2_mode0;
> + u32 com_div_frac_start3_mode0;
> + u32 com_integloop_gain0_mode0;
> + u32 com_integloop_gain1_mode0;
> + u32 com_lock_cmp_en;
> + u32 com_lock_cmp1_mode0;
> + u32 com_lock_cmp2_mode0;
> + u32 com_lock_cmp3_mode0;
> + u32 com_core_clk_en;
> + u32 com_coreclk_div_mode0;
> +
> + u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL];
> + u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL];
> + u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL];
> + u32 tx_lx_pre_driver_1[HDMI_NUM_TX_CHANNEL];
> + u32 tx_lx_pre_driver_2[HDMI_NUM_TX_CHANNEL];
> + u32 tx_lx_res_code_offset[HDMI_NUM_TX_CHANNEL];
> +
> + u32 phy_mode;
> +};
> +
> +struct hdmi_8998_post_divider {
> + u64 vco_freq;
> + int hsclk_divsel;
> + int vco_ratio;
> + int tx_band_sel;
> + int half_rate_mode;
> +};
> +
> +static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8998 *pll)
> +{
> + return platform_get_drvdata(pll->pdev);
> +}
> +
> +static inline void hdmi_pll_write(struct hdmi_pll_8998 *pll, int offset,
> + u32 data)
> +{
> + writel(data, pll->mmio_qserdes_com + offset);
> +}
> +
> +static inline u32 hdmi_pll_read(struct hdmi_pll_8998 *pll, int offset)
> +{
> + return readl(pll->mmio_qserdes_com + offset);
> +}
> +
> +static inline void hdmi_tx_chan_write(struct hdmi_pll_8998 *pll, int channel,
> + int offset, int data)
> +{
> + writel(data, pll->mmio_qserdes_tx[channel] + offset);
> +}
> +
> +static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk,
> + bool gen_ssc)
> +{
> + if ((frac_start != 0) || gen_ssc)
> + return 0x8;
> +
> + return 0x30;
> +}
> +
> +static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc)
> +{
> + if ((frac_start != 0) || gen_ssc)
> + return 0x16;
> +
> + return 0x18;
> +}
> +
> +static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc)
> +{
> + if ((frac_start != 0) || gen_ssc)
> + return 0x34;
> +
> + return 0x2;
> +}
> +
> +static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref_clk,
> + bool gen_ssc)
> +{
> + int digclk_divsel = bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;
> + u64 base;
> +
> + if ((frac_start != 0) || gen_ssc)
> + base = 0x3F;
> + else
> + base = 0xC4;
> +
> + base <<= (digclk_divsel == 2 ? 1 : 0);
> +
> + return (base <= 2046 ? base : 2046);
> +}
> +
> +static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)
> +{
> + u64 dividend = HDMI_PLL_CMP_CNT * fdata;
> + u32 divisor = ref_clk * 10;
> + u32 rem;
> +
> + rem = do_div(dividend, divisor);
> + if (rem > (divisor >> 1))
> + dividend++;
> +
> + return dividend - 1;
> +}
> +
> +static inline u64 pll_cmp_to_fdata(u32 pll_cmp, unsigned long ref_clk)
> +{
> + u64 fdata = ((u64)pll_cmp) * ref_clk * 10;
> +
> + do_div(fdata, HDMI_PLL_CMP_CNT);
> +
> + return fdata;
> +}
> +
> +#define HDMI_REF_CLOCK_HZ ((u64)19200000)
> +#define HDMI_MHZ_TO_HZ ((u64)1000000)
> +static int pll_get_post_div(struct hdmi_8998_post_divider *pd, u64 bclk)
> +{
> + u32 const ratio_list[] = {1, 2, 3, 4, 5, 6,
> + 9, 10, 12, 15, 25};
> + u32 const band_list[] = {0, 1, 2, 3};
> + u32 const sz_ratio = ARRAY_SIZE(ratio_list);
> + u32 const sz_band = ARRAY_SIZE(band_list);
> + u32 const cmp_cnt = 1024;
> + u32 const th_min = 500, th_max = 1000;
> + u32 half_rate_mode = 0;
> + u32 list_elements;
> + int optimal_index;
> + u32 i, j, k;
> + u32 found_hsclk_divsel = 0, found_vco_ratio;
> + u32 found_tx_band_sel;
> + u64 const min_freq = HDMI_VCO_MIN_FREQ, max_freq = HDMI_VCO_MAX_FREQ;
> + u64 freq_list[ARRAY_SIZE(ratio_list) * ARRAY_SIZE(band_list)];
> + u64 found_vco_freq;
> + u64 freq_optimal;
> +
> +find_optimal_index:
> + freq_optimal = max_freq;
> + optimal_index = -1;
> + list_elements = 0;
> +
> + for (i = 0; i < sz_ratio; i++) {
> + for (j = 0; j < sz_band; j++) {
> + u64 freq = div_u64(bclk, (1 << half_rate_mode));
> +
> + freq *= (ratio_list[i] * (1 << band_list[j]));
> + freq_list[list_elements++] = freq;
> + }
> + }
> +
> + for (k = 0; k < ARRAY_SIZE(freq_list); k++) {
> + u32 const clks_pll_div = 2, core_clk_div = 5;
> + u32 const rng1 = 16, rng2 = 8;
> + u32 th1, th2;
> + u64 core_clk, rvar1, rem;
> +
> + core_clk = (((freq_list[k] /
> + ratio_list[k / sz_band]) /
> + clks_pll_div) / core_clk_div);
This causes linking errors on arm32:
/usr/bin/arm-linux-gnueabi-ld: drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.o:
in function `pll_get_post_div':
drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c:207: undefined reference to
`__aeabi_uldivmod'
I'll replace this with div_u64. If it results in wrong frequencies being
selected, please post a separate fix for 6.12-rc
--
With best wishes
Dmitry
More information about the Freedreno
mailing list