diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index e276b8ca213c..3066dc9ebc64 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -32,26 +32,43 @@ struct hibmc_display_panel_pll { struct hibmc_dislay_pll_config { u64 hdisplay; u64 vdisplay; + int clock; u32 pll1_config_value; u32 pll2_config_value; }; static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { - {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, - {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, - {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, - {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, - {1280, 768, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, - {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, - {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, - {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, - {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, - {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, + {640, 480, 25000, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, + {800, 600, 40000, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, + {1024, 768, 65000, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, + {1152, 864, 78750, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, + {1280, 768, 80000, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, + {1280, 720, 74375, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, + {1280, 960, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1280, 1024, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1440, 900, 105952, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, + {1600, 900, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1600, 1200, 162500, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, + {1920, 1080, 148750, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, + {1920, 1200, 193750, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, }; +static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode) +{ + int i, diff; + + for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { + if (hibmc_pll_table[i].hdisplay == mode->hdisplay && + hibmc_pll_table[i].vdisplay == mode->vdisplay) { + diff = abs(mode->clock - hibmc_pll_table[i].clock); + if (diff < mode->clock / 100) /* tolerance 1/100 */ + return i; + } + } + + return -MODE_CLOCK_RANGE; +} + static int hibmc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) { @@ -214,19 +231,15 @@ static enum drm_mode_status hibmc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) { - size_t i = 0; int vrefresh = drm_mode_vrefresh(mode); if (vrefresh < 59 || vrefresh > 61) return MODE_NOCLOCK; - for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { - if (hibmc_pll_table[i].hdisplay == mode->hdisplay && - hibmc_pll_table[i].vdisplay == mode->vdisplay) - return MODE_OK; - } + if (hibmc_get_best_clock_idx(mode) >= 0) + return MODE_OK; - return MODE_BAD; + return MODE_CLOCK_RANGE; } static u32 format_pll_reg(void) @@ -281,23 +294,20 @@ static void set_vclock_hisilicon(struct drm_device *dev, u64 pll) writel(val, priv->mmio + CRT_PLL1_HS); } -static void get_pll_config(u64 x, u64 y, u32 *pll1, u32 *pll2) +static void get_pll_config(struct drm_display_mode *mode, u32 *pll1, u32 *pll2) { - size_t i; - size_t count = ARRAY_SIZE(hibmc_pll_table); + int idx; - for (i = 0; i < count; i++) { - if (hibmc_pll_table[i].hdisplay == x && - hibmc_pll_table[i].vdisplay == y) { - *pll1 = hibmc_pll_table[i].pll1_config_value; - *pll2 = hibmc_pll_table[i].pll2_config_value; - return; - } + idx = hibmc_get_best_clock_idx(mode); + if (idx < 0) { + /* if found none, we use default value */ + *pll1 = CRT_PLL1_HS_25MHZ; + *pll2 = CRT_PLL2_HS_25MHZ; + return; } - /* if found none, we use default value */ - *pll1 = CRT_PLL1_HS_25MHZ; - *pll2 = CRT_PLL2_HS_25MHZ; + *pll1 = hibmc_pll_table[idx].pll1_config_value; + *pll2 = hibmc_pll_table[idx].pll2_config_value; } /* @@ -319,7 +329,7 @@ static u32 display_ctrl_adjust(struct drm_device *dev, x = mode->hdisplay; y = mode->vdisplay; - get_pll_config(x, y, &pll1, &pll2); + get_pll_config(mode, &pll1, &pll2); writel(pll2, priv->mmio + CRT_PLL2_HS); set_vclock_hisilicon(dev, pll1);