On 24/07/2024 18:01, Marc Gonzalez wrote:
From: Arnaud Vrac <av...@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 <av...@freebox.fr>
Reviewed-by: Dmitry Baryshkov <dmitry.barysh...@linaro.org>
Signed-off-by: Marc Gonzalez <mgonza...@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

Reply via email to