diff -ur a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c 2018-06-02 16:39:27.777402009 +0200 +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c 2018-06-02 17:25:33.803332054 +0200 @@ -588,22 +588,28 @@ /* Finally filter by configurable vclk frequencies */ switch (vclk_freq) { case 25175: - case 40000: + case 29750: case 32000: case 36000: case 33750: case 33900: + case 40000: case 54000: case 65000: case 74250: + case 106500: case 108000: case 148500: case 162000: + case 193250: case 297000: case 594000: return MODE_OK; } + if (meson_vclk_supported(vclk_freq)) + return MODE_OK; + return MODE_CLOCK_RANGE; } diff -ur a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c --- a/drivers/gpu/drm/meson/meson_vclk.c 2018-06-02 20:16:44.008061996 +0200 +++ b/drivers/gpu/drm/meson/meson_vclk.c 2018-06-04 03:26:22.092619417 +0200 @@ -321,39 +321,49 @@ } -/* PLL O1 O2 O3 VP DV EN TX */ -/* 4320 /4 /4 /1 /5 /1 => /2 /2 */ -#define MESON_VCLK_HDMI_ENCI_54000 1 -/* 4320 /4 /4 /1 /5 /1 => /1 /2 */ -#define MESON_VCLK_HDMI_DDR_54000 2 -/* 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 -/* 2560 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_32000 5 -/* 2700 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_33750 6 -/* 2712 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_33900 7 -/* 2880 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_36000 8 -/* 3200 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_40000 9 -/* 5200 /4 /2 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_65000 10 -/* 2970 /2 /2 /2 /5 /1 => /1 /1 */ -#define MESON_VCLK_HDMI_74250 11 -/* 4320 /4 /1 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_108000 12 -/* 2970 /1 /2 /2 /5 /1 => /1 /1 */ -#define MESON_VCLK_HDMI_148500 13 -/* 3240 /2 /1 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_162000 14 -/* 2970 /1 /1 /1 /5 /2 => /1 /1 */ -#define MESON_VCLK_HDMI_297000 15 -/* 5940 /1 /1 /2 /5 /1 => /1 /1 */ -#define MESON_VCLK_HDMI_594000 16 +enum { + /* PLL O1 O2 O3 VP DV EN TX */ + /* 4320 /4 /4 /1 /5 /1 => /2 /2 */ + MESON_VCLK_HDMI_ENCI_54000 = 1, + /* 4320 /4 /4 /1 /5 /1 => /1 /2 */ + MESON_VCLK_HDMI_DDR_54000, + /* 2970 /4 /1 /1 /5 /1 => /1 /2 */ + MESON_VCLK_HDMI_DDR_148500, + /* 4028 /4 /4 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_25175, + /* 4760 /4 /4 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_29750, + /* 2560 /4 /2 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_32000, + /* 2700 /4 /2 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_33750, + /* 2712 /4 /2 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_33900, + /* 2880 /4 /2 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_36000, + /* 3200 /4 /2 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_40000, + /* 5200 /4 /2 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_65000, + /* 2970 /2 /2 /2 /5 /1 => /1 /1 */ + MESON_VCLK_HDMI_74250, + /* 4260 /4 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_106500, + /* 4320 /4 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_108000, + /* 2970 /1 /2 /2 /5 /1 => /1 /1 */ + MESON_VCLK_HDMI_148500, + /* 3240 /2 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_162000, + /* 3865 /2 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_193250, + /* 2970 /1 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_297000, + /* 5940 /1 /1 /2 /5 /1 => /1 /1 */ + MESON_VCLK_HDMI_594000, + /* custom calculated mode */ + MESON_VCLK_HDMI_CUSTOM, +}; struct meson_vclk_params { unsigned int pll_base_freq; @@ -427,6 +437,14 @@ .vid_pll_div = VID_PLL_DIV_5, .vclk_div = 2, }, + [MESON_VCLK_HDMI_29750] = { + .pll_base_freq = 4760000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 2, + }, [MESON_VCLK_HDMI_32000] = { .pll_base_freq = 5120000, .pll_od1 = 4, @@ -475,6 +493,14 @@ .vid_pll_div = VID_PLL_DIV_5, .vclk_div = 2, }, + [MESON_VCLK_HDMI_106500] = { + .pll_base_freq = 4260000, + .pll_od1 = 4, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 2, + }, [MESON_VCLK_HDMI_108000] = { .pll_base_freq = 4320000, .pll_od1 = 4, @@ -491,6 +517,14 @@ .vid_pll_div = VID_PLL_DIV_5, .vclk_div = 2, }, + [MESON_VCLK_HDMI_193250] = { + .pll_base_freq = 3865000, + .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) @@ -519,6 +553,18 @@ unsigned int val; if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + unsigned int step = 48000; + unsigned int range = 0x1000; + unsigned int round = step / range / 2; + unsigned int frac = base % step; + unsigned int m = (base - frac) / step; + + frac = (frac + round) * range / step; + if (frac >= range) + frac = range-1; + + dev_info(priv->dev, "%s: base=%d, m=0x%.2x, frac=0x%.3x\n", __func__, base, m, frac); + switch (base) { case 2700000: regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000238); @@ -755,10 +801,49 @@ /* 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, 0x4555); + break; + + default: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000200 | m); + 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); + + /* div_frac */ + if (frac) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0xFFFF, 0x4000 | frac); break; + }; } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + unsigned int step = 24000; + unsigned int range = 0x400; + unsigned int round = step / range / 2; + unsigned int frac = base % step; + unsigned int m = (base - frac) / step; + + frac = (frac + round) * range / step; + if (frac >= range) + frac = range-1; + + pr_info("%s: base=%d, m=0x%.2x, frac=0x%.3x", __func__, base, m, frac); + switch (base) { case 2700000: regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000270); @@ -868,6 +953,14 @@ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); break; + default: + 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); + break; }; /* Reset PLL */ @@ -906,6 +999,151 @@ 3 << 19, pll_od_to_reg(od3) << 19); } +unsigned int meson_vclk_freq_from_params(struct meson_vclk_params* vclk_params) +{ + unsigned int vclk_freq = vclk_params->pll_base_freq / + vclk_params->pll_od1 / vclk_params->pll_od2 / + vclk_params->pll_od3 / vclk_params->vclk_div; + + switch (vclk_params->vid_pll_div) { + case VID_PLL_DIV_2: + vclk_freq /= 2; + break; + case VID_PLL_DIV_2p5: + vclk_freq *= 4; + vclk_freq /= 10; + break; + case VID_PLL_DIV_3: + vclk_freq /= 3; + break; + case VID_PLL_DIV_3p5: + vclk_freq *= 2; + vclk_freq /= 7; + break; + case VID_PLL_DIV_3p75: + vclk_freq *= 4; + vclk_freq /= 15; + break; + case VID_PLL_DIV_4: + vclk_freq /= 4; + break; + case VID_PLL_DIV_5: + vclk_freq /= 5; + break; + case VID_PLL_DIV_6: + vclk_freq /= 6; + break; + case VID_PLL_DIV_6p25: + vclk_freq *= 8; + vclk_freq /= 50; + break; + case VID_PLL_DIV_7: + vclk_freq /= 7; + break; + case VID_PLL_DIV_7p5: + vclk_freq *= 4; + vclk_freq /= 30; + break; + case VID_PLL_DIV_12: + vclk_freq /= 12; + break; + case VID_PLL_DIV_14: + vclk_freq /= 14; + break; + case VID_PLL_DIV_15: + vclk_freq /= 15; + break; + default: + return 0; + } + + return vclk_freq; +} + +static bool meson_get_vclk_params(unsigned int freq, + struct meson_vclk_params* vclk_params) +{ + unsigned int vclk_freq = freq; + unsigned int base_freq, od1_od2, od3_vpll_vclk; + int p, jit; + bool found = false; + + if ((vclk_freq < 13500) || (vclk_freq > 594000)) + return false; + + if (vclk_freq % 25 > 12) + vclk_freq += 25; + vclk_freq -= vclk_freq % 25; + + for (p = 1; p < sizeof(params) / sizeof(struct meson_vclk_params); p++) { + if (params[p].vid_pll_div != VID_PLL_DIV_5) + continue; + + od1_od2 = params[p].pll_od1 * params[p].pll_od2; + od3_vpll_vclk = params[p].pll_od3 * 5 * params[p].vclk_div; + + if ((od3_vpll_vclk != 10) && (vclk_freq > 54000 + 175)) + continue; + + base_freq = vclk_freq * od1_od2 * od3_vpll_vclk; + + if ((base_freq < 2700000) || (base_freq > 5940000)) + continue; + + vclk_params->pll_base_freq = base_freq; + vclk_params->pll_od1 = params[p].pll_od1; + vclk_params->pll_od2 = params[p].pll_od2; + vclk_params->pll_od3 = params[p].pll_od3; + vclk_params->vid_pll_div = params[p].vid_pll_div; + vclk_params->vclk_div = params[p].vclk_div; + + found = true; + + if (params[p].pll_base_freq > vclk_params->pll_base_freq) + jit = params[p].pll_base_freq - vclk_params->pll_base_freq; + else + jit = vclk_params->pll_base_freq - params[p].pll_base_freq; + + if (jit < 175 * od1_od2 * od3_vpll_vclk) { + vclk_params->pll_base_freq = params[p].pll_base_freq; + vclk_freq = vclk_params->pll_base_freq / od1_od2 / od3_vpll_vclk; + } + + if (jit < 40000) + break; + } + + return found; +} + +static void meson_print_vclk_params(struct meson_drm *priv, unsigned int freq, + struct meson_vclk_params* vclk_params) +{ + unsigned int vclk_freq = meson_vclk_freq_from_params(vclk_params); + + if (vclk_params->vid_pll_div != VID_PLL_DIV_5) + return; + + if (vclk_freq != freq) + dev_info(priv->dev, "adjusted %d -> %d", freq, vclk_freq); + + dev_info(priv->dev, "params %d / %d / %d / %d / %d / %d = %d\n", + vclk_params->pll_base_freq, + vclk_params->pll_od1, + vclk_params->pll_od2, + vclk_params->pll_od3, + 5, vclk_params->vclk_div, + vclk_freq); +} + +bool meson_vclk_supported(unsigned int vclk_freq) +{ + struct meson_vclk_params custom_params; + + return (meson_get_vclk_params(vclk_freq, &custom_params)); +} +EXPORT_SYMBOL_GPL(meson_vclk_supported); + 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) @@ -913,6 +1151,8 @@ unsigned int freq; unsigned int hdmi_tx_div; unsigned int venc_div; + struct meson_vclk_params custom_params; + struct meson_vclk_params* vclk_params; if (target == MESON_VCLK_TARGET_CVBS) { meson_venci_cvbs_clock_config(priv); @@ -945,6 +1185,9 @@ case 25175: freq = MESON_VCLK_HDMI_25175; break; + case 29750: + freq = MESON_VCLK_HDMI_29750; + break; case 32000: freq = MESON_VCLK_HDMI_32000; break; @@ -966,6 +1209,9 @@ case 74250: freq = MESON_VCLK_HDMI_74250; break; + case 106500: + freq = MESON_VCLK_HDMI_106500; + break; case 108000: freq = MESON_VCLK_HDMI_108000; break; @@ -978,6 +1224,9 @@ case 162000: freq = MESON_VCLK_HDMI_162000; break; + case 193250: + freq = MESON_VCLK_HDMI_193250; + break; case 297000: freq = MESON_VCLK_HDMI_297000; break; @@ -985,9 +1234,8 @@ freq = MESON_VCLK_HDMI_594000; break; default: - pr_err("Fatal Error, invalid HDMI vclk freq %d\n", - vclk_freq); - return; + freq = MESON_VCLK_HDMI_CUSTOM; + break; } /* Set HDMI-TX sys clock */ @@ -998,20 +1246,35 @@ regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN); + vclk_params = NULL; + + if (freq != MESON_VCLK_HDMI_CUSTOM) + vclk_params = ¶ms[freq]; + else if (meson_get_vclk_params(vclk_freq, &custom_params)) + vclk_params = &custom_params; + + if (!vclk_params) { + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", + vclk_freq); + return; + } + + meson_print_vclk_params(priv, vclk_freq, vclk_params); + /* 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); + meson_hdmi_pll_set(priv, vclk_params->pll_base_freq, + vclk_params->pll_od1, + vclk_params->pll_od2, + vclk_params->pll_od3); /* Setup vid_pll divider */ - meson_vid_pll_set(priv, params[freq].vid_pll_div); + meson_vid_pll_set(priv, vclk_params->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_params->vclk_div - 1); /* Set HDMI-TX source */ switch (hdmi_tx_div) { diff -ur a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h --- a/drivers/gpu/drm/meson/meson_vclk.h 2018-05-26 22:29:37.112948771 +0200 +++ b/drivers/gpu/drm/meson/meson_vclk.h 2018-05-31 05:15:53.342134304 +0200 @@ -29,6 +29,8 @@ /* 27MHz is the CVBS Pixel Clock */ #define MESON_VCLK_CVBS 27000 +bool meson_vclk_supported(unsigned int vclk_freq); + 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); diff -ur a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c --- a/drivers/gpu/drm/meson/meson_venc.c 2018-06-02 20:23:23.744646670 +0200 +++ b/drivers/gpu/drm/meson/meson_venc.c 2018-06-02 17:25:33.807332121 +0200 @@ -707,6 +707,12 @@ 752, 800, 0, 480, 490, 492, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, }, + /* 800x480@60Hz */ + { + { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 29750, 800, 824, + 896, 992, 0, 480, 483, 490, 500, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + }, /* 800x600@60Hz */ { { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, @@ -737,6 +743,12 @@ 1328, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }, + /* 1440x900@60Hz */ + { + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, + 1520, 1672, 1904, 0, 900, 903, 909, 934, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + }, /* 1600x1200@60Hz */ { { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, @@ -749,6 +761,12 @@ 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, }, + /* 1920x1200@60Hz */ + { + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, + 2056, 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + }, { }, /* sentinel */ };