From 3273fc63e1ed4866fe10a582f4203f2aa142b216 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 16 Jul 2018 09:40:14 +0200 Subject: drm/meson: Make DMT timings parameters and pixel clock generic Remove the modes timings tables for DMT modes and calculate the HW paremeters from the modes timings. Switch the DMT modes pixel clock calculation out of the static frequency list to a generic calculation from a range of possible PLL dividers. This patch is an intermediate step towards usage of the Common Clock Framwework for PLL setup, by reworking the code to have common sel_pll() function called by the CEA (HDMI) freq setup and the generic DMT frequencies setup, we should be able to simply call clk_set_rate() on the PLL clock handle in a near future. The CEA (HDMI) and CVBS modes needs very specific clock paths that CCF will never be able to determine by itself, so there is still some work to do for a full handoff to CCF handling the clocks. This setup permits setting non-CEA modes like : - 1600x900-60Hz - 1280x1024-75Hz - 1280x1024-60Hz - 1440x900-60Hz - 1366x768-60Hz - 1280x800-60Hz - 1152x864-75Hz - 1024x768-75Hz - 1024x768-70Hz - 1024x768-60Hz - 832x624-75Hz - 800x600-75Hz - 800x600-72Hz - 800x600-60Hz - 640x480-75Hz - 640x480-73Hz - 640x480-67Hz Signed-off-by: Neil Armstrong Acked-by: Jerome Brunet [narmstrong: fixed trivial checkpatch issues] Link: https://patchwork.freedesktop.org/patch/msgid/1531726814-14638-1-git-send-email-narmstrong@baylibre.com --- drivers/gpu/drm/meson/meson_vclk.c | 656 ++++++++++++++++--------------------- 1 file changed, 286 insertions(+), 370 deletions(-) (limited to 'drivers/gpu/drm/meson/meson_vclk.c') diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c index f0511220317f..ae5473257f72 100644 --- a/drivers/gpu/drm/meson/meson_vclk.c +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -320,32 +320,23 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) CTS_VDAC_EN, CTS_VDAC_EN); } - +enum { /* PLL O1 O2 O3 VP DV EN TX */ /* 4320 /4 /4 /1 /5 /1 => /2 /2 */ -#define MESON_VCLK_HDMI_ENCI_54000 1 + MESON_VCLK_HDMI_ENCI_54000 = 1, /* 4320 /4 /4 /1 /5 /1 => /1 /2 */ -#define MESON_VCLK_HDMI_DDR_54000 2 + MESON_VCLK_HDMI_DDR_54000, /* 2970 /4 /1 /1 /5 /1 => /1 /2 */ -#define MESON_VCLK_HDMI_DDR_148500 3 -/* 4028 /4 /4 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_25175 4 -/* 3200 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_40000 5 -/* 5200 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_65000 6 + MESON_VCLK_HDMI_DDR_148500, /* 2970 /2 /2 /2 /5 /1 => /1 /1 */ -#define MESON_VCLK_HDMI_74250 7 -/* 4320 /4 /1 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_108000 8 + MESON_VCLK_HDMI_74250, /* 2970 /1 /2 /2 /5 /1 => /1 /1 */ -#define MESON_VCLK_HDMI_148500 9 -/* 3240 /2 /1 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_162000 10 + MESON_VCLK_HDMI_148500, /* 2970 /1 /1 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_297000 11 + MESON_VCLK_HDMI_297000, /* 5940 /1 /1 /2 /5 /1 => /1 /1 */ -#define MESON_VCLK_HDMI_594000 12 + MESON_VCLK_HDMI_594000 +}; struct meson_vclk_params { unsigned int pll_base_freq; @@ -411,46 +402,6 @@ struct meson_vclk_params { .vid_pll_div = VID_PLL_DIV_5, .vclk_div = 1, }, - [MESON_VCLK_HDMI_25175] = { - .pll_base_freq = 4028000, - .pll_od1 = 4, - .pll_od2 = 4, - .pll_od3 = 1, - .vid_pll_div = VID_PLL_DIV_5, - .vclk_div = 2, - }, - [MESON_VCLK_HDMI_40000] = { - .pll_base_freq = 3200000, - .pll_od1 = 4, - .pll_od2 = 2, - .pll_od3 = 1, - .vid_pll_div = VID_PLL_DIV_5, - .vclk_div = 2, - }, - [MESON_VCLK_HDMI_65000] = { - .pll_base_freq = 5200000, - .pll_od1 = 4, - .pll_od2 = 2, - .pll_od3 = 1, - .vid_pll_div = VID_PLL_DIV_5, - .vclk_div = 2, - }, - [MESON_VCLK_HDMI_108000] = { - .pll_base_freq = 4320000, - .pll_od1 = 4, - .pll_od2 = 1, - .pll_od3 = 1, - .vid_pll_div = VID_PLL_DIV_5, - .vclk_div = 2, - }, - [MESON_VCLK_HDMI_162000] = { - .pll_base_freq = 3240000, - .pll_od1 = 2, - .pll_od2 = 1, - .pll_od3 = 1, - .vid_pll_div = VID_PLL_DIV_5, - .vclk_div = 2, - }, }; static inline unsigned int pll_od_to_reg(unsigned int od) @@ -470,358 +421,217 @@ static inline unsigned int pll_od_to_reg(unsigned int od) return 0; } -void meson_hdmi_pll_set(struct meson_drm *priv, - unsigned int base, - unsigned int od1, - unsigned int od2, - unsigned int od3) +void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m, + unsigned int frac, unsigned int od1, + unsigned int od2, unsigned int od3) { unsigned int val; if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { - switch (base) { - case 2970000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* Enable and unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - 0x7 << 28, 0x4 << 28); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - - /* div_frac */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 0xFFFF, 0x4e00); - break; - - case 3200000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000242); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - - /* div_frac */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 0xFFFF, 0x4aab); - break; - - case 3240000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000243); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - - /* div_frac */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 0xFFFF, 0x4800); - break; - - case 3865000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000250); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - - /* div_frac */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 0xFFFF, 0x4855); - break; - - case 4028000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000253); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - - /* div_frac */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 0xFFFF, 0x4eab); - break; - - case 4320000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800025a); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - break; + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000200 | m); + if (frac) + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0x00004000 | frac); + else + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - case 5940000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800027b); - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 0xFFFF, 0x4c00); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - break; + /* Enable and unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 0x7 << 28, 0x4 << 28); - case 5200000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800026c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); - - /* unreset */ - regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - BIT(28), 0); - - /* Poll for lock bit */ - regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, - val, (val & HDMI_PLL_LOCK), 10, 0); - break; - }; + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { - switch (base) { - case 2970000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 3200000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000285); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb155); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 3240000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000287); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 3865000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002a1); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb02b); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 4028000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002a7); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb355); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 4320000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002b4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 5940000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002f7); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb200); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - case 5200000: - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002d8); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb2ab); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); - regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); - break; - - }; + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000200 | m); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000 | frac); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); /* Reset PLL */ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - HDMI_PLL_RESET, HDMI_PLL_RESET); + HDMI_PLL_RESET, HDMI_PLL_RESET); regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, - HDMI_PLL_RESET, 0); + HDMI_PLL_RESET, 0); /* Poll for lock bit */ regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, (val & HDMI_PLL_LOCK), 10, 0); - }; + } if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 3 << 16, pll_od_to_reg(od1) << 16); + 3 << 16, pll_od_to_reg(od1) << 16); else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || - meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, - 3 << 21, pll_od_to_reg(od1) << 21); + 3 << 21, pll_od_to_reg(od1) << 21); if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 3 << 22, pll_od_to_reg(od2) << 22); + 3 << 22, pll_od_to_reg(od2) << 22); else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || - meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, - 3 << 23, pll_od_to_reg(od2) << 23); + 3 << 23, pll_od_to_reg(od2) << 23); if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, - 3 << 18, pll_od_to_reg(od3) << 18); + 3 << 18, pll_od_to_reg(od3) << 18); else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || - meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, - 3 << 19, pll_od_to_reg(od3) << 19); + 3 << 19, pll_od_to_reg(od3) << 19); + } -void meson_vclk_setup(struct meson_drm *priv, unsigned int target, - unsigned int vclk_freq, unsigned int venc_freq, - unsigned int dac_freq, bool hdmi_use_enci) +#define XTAL_FREQ 24000 + +static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv, + unsigned int pll_freq) { - unsigned int freq; - unsigned int hdmi_tx_div; - unsigned int venc_div; + /* The GXBB PLL has a /2 pre-multiplier */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + pll_freq /= 2; - if (target == MESON_VCLK_TARGET_CVBS) { - meson_venci_cvbs_clock_config(priv); - return; + return pll_freq / XTAL_FREQ; +} + +#define HDMI_FRAC_MAX_GXBB 4096 +#define HDMI_FRAC_MAX_GXL 1024 + +static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv, + unsigned int m, + unsigned int pll_freq) +{ + unsigned int parent_freq = XTAL_FREQ; + unsigned int frac_max = HDMI_FRAC_MAX_GXL; + unsigned int frac_m; + unsigned int frac; + + /* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + frac_max = HDMI_FRAC_MAX_GXBB; + parent_freq *= 2; } - hdmi_tx_div = vclk_freq / dac_freq; + /* We can have a perfect match !*/ + if (pll_freq / m == parent_freq && + pll_freq % m == 0) + return 0; - if (hdmi_tx_div == 0) { - pr_err("Fatal Error, invalid HDMI-TX freq %d\n", - dac_freq); - return; + frac = div_u64((u64)pll_freq * (u64)frac_max, parent_freq); + frac_m = m * frac_max; + if (frac_m > frac) + return frac_max; + frac -= frac_m; + + return min((u16)frac, (u16)(frac_max - 1)); +} + +static bool meson_hdmi_pll_validate_params(struct meson_drm *priv, + unsigned int m, + unsigned int frac) +{ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + /* Empiric supported min/max dividers */ + if (m < 53 || m > 123) + return false; + if (frac >= HDMI_FRAC_MAX_GXBB) + return false; + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + /* Empiric supported min/max dividers */ + if (m < 106 || m > 247) + return false; + if (frac >= HDMI_FRAC_MAX_GXL) + return false; } - venc_div = vclk_freq / venc_freq; + return true; +} - if (venc_div == 0) { - pr_err("Fatal Error, invalid HDMI venc freq %d\n", - venc_freq); - return; +static bool meson_hdmi_pll_find_params(struct meson_drm *priv, + unsigned int freq, + unsigned int *m, + unsigned int *frac, + unsigned int *od) +{ + /* Cycle from /16 to /2 */ + for (*od = 16 ; *od > 1 ; *od >>= 1) { + *m = meson_hdmi_pll_get_m(priv, freq * *od); + if (!*m) + continue; + *frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od); + + DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d\n", + freq, *m, *frac, *od); + + if (meson_hdmi_pll_validate_params(priv, *m, *frac)) + return true; } - switch (vclk_freq) { - case 54000: - if (hdmi_use_enci) - freq = MESON_VCLK_HDMI_ENCI_54000; - else - freq = MESON_VCLK_HDMI_DDR_54000; - break; - case 25175: - freq = MESON_VCLK_HDMI_25175; - break; - case 40000: - freq = MESON_VCLK_HDMI_40000; - break; - case 65000: - freq = MESON_VCLK_HDMI_65000; - break; - case 74250: - freq = MESON_VCLK_HDMI_74250; - break; - case 108000: - freq = MESON_VCLK_HDMI_108000; - break; - case 148500: - if (dac_freq != 148500) - freq = MESON_VCLK_HDMI_DDR_148500; - else - freq = MESON_VCLK_HDMI_148500; - break; - case 162000: - freq = MESON_VCLK_HDMI_162000; - break; - case 297000: - freq = MESON_VCLK_HDMI_297000; - break; - case 594000: - freq = MESON_VCLK_HDMI_594000; - break; - default: - pr_err("Fatal Error, invalid HDMI vclk freq %d\n", - vclk_freq); + return false; +} + +/* pll_freq is the frequency after the OD dividers */ +enum drm_mode_status +meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq) +{ + unsigned int od, m, frac; + + /* In DMT mode, path after PLL is always /10 */ + freq *= 10; + + if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od)) + return MODE_OK; + + return MODE_CLOCK_RANGE; +} +EXPORT_SYMBOL_GPL(meson_vclk_dmt_supported_freq); + +/* pll_freq is the frequency after the OD dividers */ +static void meson_hdmi_pll_generic_set(struct meson_drm *priv, + unsigned int pll_freq) +{ + unsigned int od, m, frac, od1, od2, od3; + + if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) { + od3 = 1; + if (od < 4) { + od1 = 2; + od2 = 1; + } else { + od2 = od / 4; + od1 = od / od2; + } + + DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d/%d/%d\n", + pll_freq, m, frac, od1, od2, od3); + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + return; } + DRM_ERROR("Fatal, unable to find parameters for PLL freq %d\n", + pll_freq); +} + +static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, + unsigned int od1, unsigned int od2, unsigned int od3, + unsigned int vid_pll_div, unsigned int vclk_div, + unsigned int hdmi_tx_div, unsigned int venc_div, + bool hdmi_use_enci) +{ /* Set HDMI-TX sys clock */ regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, CTS_HDMI_SYS_SEL_MASK, 0); @@ -831,19 +641,49 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN); /* Set HDMI PLL rate */ - meson_hdmi_pll_set(priv, params[freq].pll_base_freq, - params[freq].pll_od1, - params[freq].pll_od2, - params[freq].pll_od3); + if (!od1 && !od2 && !od3) { + meson_hdmi_pll_generic_set(priv, pll_base_freq); + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + switch (pll_base_freq) { + case 2970000: + meson_hdmi_pll_set_params(priv, 0x3d, 0xe00, + od1, od2, od3); + break; + case 4320000: + meson_hdmi_pll_set_params(priv, 0x5a, 0, + od1, od2, od3); + break; + case 5940000: + meson_hdmi_pll_set_params(priv, 0x7b, 0xc00, + od1, od2, od3); + break; + } + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + switch (pll_base_freq) { + case 2970000: + meson_hdmi_pll_set_params(priv, 0x7b, 0x300, + od1, od2, od3); + break; + case 4320000: + meson_hdmi_pll_set_params(priv, 0xb4, 0, + od1, od2, od3); + break; + case 5940000: + meson_hdmi_pll_set_params(priv, 0xf7, 0x200, + od1, od2, od3); + break; + } + } /* Setup vid_pll divider */ - meson_vid_pll_set(priv, params[freq].vid_pll_div); + meson_vid_pll_set(priv, vid_pll_div); /* Set VCLK div */ regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_SEL_MASK, 0); regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, - VCLK_DIV_MASK, params[freq].vclk_div - 1); + VCLK_DIV_MASK, vclk_div - 1); /* Set HDMI-TX source */ switch (hdmi_tx_div) { @@ -981,4 +821,80 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN); } + +void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int vclk_freq, unsigned int venc_freq, + unsigned int dac_freq, bool hdmi_use_enci) +{ + unsigned int freq; + unsigned int hdmi_tx_div; + unsigned int venc_div; + + if (target == MESON_VCLK_TARGET_CVBS) { + meson_venci_cvbs_clock_config(priv); + return; + } else if (target == MESON_VCLK_TARGET_DMT) { + /* The DMT clock path is fixed after the PLL: + * - automatic PLL freq + OD management + * - vid_pll_div = VID_PLL_DIV_5 + * - vclk_div = 2 + * - hdmi_tx_div = 1 + * - venc_div = 1 + * - encp encoder + */ + meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0, + VID_PLL_DIV_5, 2, 1, 1, false); + return; + } + + hdmi_tx_div = vclk_freq / dac_freq; + + if (hdmi_tx_div == 0) { + pr_err("Fatal Error, invalid HDMI-TX freq %d\n", + dac_freq); + return; + } + + venc_div = vclk_freq / venc_freq; + + if (venc_div == 0) { + pr_err("Fatal Error, invalid HDMI venc freq %d\n", + venc_freq); + return; + } + + switch (vclk_freq) { + case 54000: + if (hdmi_use_enci) + freq = MESON_VCLK_HDMI_ENCI_54000; + else + freq = MESON_VCLK_HDMI_DDR_54000; + break; + case 74250: + freq = MESON_VCLK_HDMI_74250; + break; + case 148500: + if (dac_freq != 148500) + freq = MESON_VCLK_HDMI_DDR_148500; + else + freq = MESON_VCLK_HDMI_148500; + break; + case 297000: + freq = MESON_VCLK_HDMI_297000; + break; + case 594000: + freq = MESON_VCLK_HDMI_594000; + break; + default: + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", + vclk_freq); + return; + } + + meson_vclk_set(priv, params[freq].pll_base_freq, + params[freq].pll_od1, params[freq].pll_od2, + params[freq].pll_od3, params[freq].vid_pll_div, + params[freq].vclk_div, hdmi_tx_div, venc_div, + hdmi_use_enci); +} EXPORT_SYMBOL_GPL(meson_vclk_setup); -- cgit v1.2.3