[PATCH 33/49] staging: hikey9xx/gpu: re-work the mode validation code

From: Mauro Carvalho Chehab
Date: Wed Aug 19 2020 - 08:04:37 EST


Do some cleanups at the mode validation code. Right now, there
is a known issue with this driver which makes it to set up
some invalid modes, depending on the display.

We don't know yet what the issue is, so, instead, let's add
a table with the modes which are known to work.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@xxxxxxxxxx>
---
.../staging/hikey9xx/gpu/kirin9xx_drm_dss.c | 274 +++++++++++-------
.../hikey9xx/gpu/kirin9xx_dw_drm_dsi.c | 34 +++
2 files changed, 211 insertions(+), 97 deletions(-)

diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
index 26212c130b79..0844bf372ca8 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
@@ -103,8 +103,9 @@ u32 dss_get_format(u32 pixel_format)
}

/*******************************************************************************
-**
-*/
+ **
+ */
+
int hdmi_ceil(uint64_t a, uint64_t b)
{
if (b == 0)
@@ -117,99 +118,108 @@ int hdmi_ceil(uint64_t a, uint64_t b)
}
}

-int hdmi_pxl_ppll7_init(struct dss_hw_ctx *ctx, uint64_t pixel_clock)
+int hdmi_pxl_ppll7_init(struct dss_hw_ctx *ctx, u64 pixel_clock)
{
- uint64_t refdiv, fbdiv, frac, postdiv1, postdiv2;
- uint64_t vco_min_freq_output = KIRIN970_VCO_MIN_FREQ_OUPUT;
- uint64_t sys_clock_fref = KIRIN970_SYS_19M2;
- uint64_t ppll7_freq_divider;
- uint64_t vco_freq_output;
- uint64_t frac_range = 0x1000000;/*2^24*/
- uint64_t pixel_clock_ori;
- uint64_t pixel_clock_cur;
- uint32_t ppll7ctrl0;
- uint32_t ppll7ctrl1;
- uint32_t ppll7ctrl0_val;
- uint32_t ppll7ctrl1_val;
- int i, ret;
+ u64 vco_min_freq_output = KIRIN970_VCO_MIN_FREQ_OUPUT;
+ u64 refdiv, fbdiv, frac, postdiv1 = 0, postdiv2 = 0;
+ u64 dss_pxl0_clk = 7 * 144000000UL;
+ u64 sys_clock_fref = KIRIN970_SYS_19M2;
+ u64 ppll7_freq_divider;
+ u64 vco_freq_output;
+ u64 frac_range = 0x1000000;/*2^24*/
+ u64 pixel_clock_ori;
+ u64 pixel_clock_cur;
+ u32 ppll7ctrl0;
+ u32 ppll7ctrl1;
+ u32 ppll7ctrl0_val;
+ u32 ppll7ctrl1_val;
int ceil_temp;
- int freq_divider_list[22] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
- 12, 14, 15, 16, 20, 21, 24,
- 25, 30, 36, 42, 49};
-
- int postdiv1_list[22] = {1, 2, 3, 4, 5, 6, 7, 4, 3, 5,
- 4, 7, 5, 4, 5, 7, 6, 5, 6, 6,
- 7, 7};
-
- int postdiv2_list[22] = {1, 1, 1, 1, 1, 1, 1, 2, 3, 2,
+ int i, ret;
+ const int freq_divider_list[22] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 12, 14, 15, 16, 20, 21,
+ 24, 25, 30, 36, 42, 49
+ };
+ const int postdiv1_list[22] = {
+ 1, 2, 3, 4, 5, 6, 7, 4, 3, 5,
+ 4, 7, 5, 4, 5, 7, 6, 5, 6, 6,
+ 7, 7
+ };
+ const int postdiv2_list[22] = {
+ 1, 1, 1, 1, 1, 1, 1, 2, 3, 2,
3, 2, 3, 4, 4, 3, 4, 5, 5, 6,
- 6, 7};
- ret = 0;
- postdiv1 = 0;
- postdiv2 = 0;
- if (pixel_clock == 0)
- return -EINVAL;
+ 6, 7
+ };

- if (ctx == NULL) {
- DRM_ERROR("NULL Pointer\n");
+ if (!pixel_clock) {
+ DRM_ERROR("Pixel clock can't be zero!\n");
return -EINVAL;
}

pixel_clock_ori = pixel_clock;
+ pixel_clock_cur = pixel_clock_ori;

- if (pixel_clock_ori <= 255000000)
- pixel_clock_cur = pixel_clock * 7;
- else if (pixel_clock_ori <= 415000000)
- pixel_clock_cur = pixel_clock * 5;
- else if (pixel_clock_ori <= 594000000)
- pixel_clock_cur = pixel_clock * 3;
- else {
- DRM_ERROR("Clock don't support!!\n");
+ if (pixel_clock_ori <= 255000000) {
+ pixel_clock_cur *= 7;
+ dss_pxl0_clk /= 7;
+ } else if (pixel_clock_ori <= 415000000) {
+ pixel_clock_cur *= 5;
+ dss_pxl0_clk /= 5;
+ } else if (pixel_clock_ori <= 594000000) {
+ pixel_clock_cur *= 3;
+ dss_pxl0_clk /= 3;
+ } else {
+ DRM_ERROR("Clock not supported!\n");
return -EINVAL;
}

pixel_clock_cur = pixel_clock_cur / 1000;
ceil_temp = hdmi_ceil(vco_min_freq_output, pixel_clock_cur);

- if (ceil_temp < 0)
+ if (ceil_temp < 0) {
+ DRM_ERROR("pixel_clock_cur can't be zero!\n");
return -EINVAL;
+ }

- ppll7_freq_divider = (uint64_t)ceil_temp;
+ ppll7_freq_divider = (u64)ceil_temp;

- for (i = 0; i < 22; i++) {
+ for (i = 0; i < ARRAY_SIZE(freq_divider_list); i++) {
if (freq_divider_list[i] >= ppll7_freq_divider) {
ppll7_freq_divider = freq_divider_list[i];
postdiv1 = postdiv1_list[i];
postdiv2 = postdiv2_list[i];
- DRM_INFO("postdiv1=0x%llx, POSTDIV2=0x%llx\n", postdiv1, postdiv2);
break;
}
}

+ if (i == ARRAY_SIZE(freq_divider_list)) {
+ DRM_ERROR("Can't find a valid setting for PLL7!\n");
+ return -EINVAL;
+ }
+
vco_freq_output = ppll7_freq_divider * pixel_clock_cur;
- if (vco_freq_output == 0)
+ if (!vco_freq_output) {
+ DRM_ERROR("Can't find a valid setting for VCO_FREQ!\n");
return -EINVAL;
+ }

ceil_temp = hdmi_ceil(400000, vco_freq_output);
-
- if (ceil_temp < 0)
+ if (ceil_temp < 0) {
+ DRM_ERROR("Can't find a valid setting for PLL7!\n");
return -EINVAL;
+ }

refdiv = ((vco_freq_output * ceil_temp) >= 494000) ? 1 : 2;
- DRM_DEBUG("refdiv=0x%llx\n", refdiv);
-
fbdiv = (vco_freq_output * ceil_temp) * refdiv / sys_clock_fref;
- DRM_DEBUG("fbdiv=0x%llx\n", fbdiv);

- frac = (uint64_t)(ceil_temp * vco_freq_output - sys_clock_fref / refdiv * fbdiv) * refdiv * frac_range;
- frac = (uint64_t)frac / sys_clock_fref;
- DRM_DEBUG("frac=0x%llx\n", frac);
+ frac = (u64)(ceil_temp * vco_freq_output - sys_clock_fref / refdiv * fbdiv) * refdiv * frac_range;
+ frac = (u64)frac / sys_clock_fref;

ppll7ctrl0 = inp32(ctx->pmctrl_base + MIDIA_PPLL7_CTRL0);
ppll7ctrl0 &= ~MIDIA_PPLL7_FREQ_DEVIDER_MASK;

ppll7ctrl0_val = 0x0;
- ppll7ctrl0_val |= (uint32_t)(postdiv2 << 23 | postdiv1 << 20 | fbdiv << 8 | refdiv << 2);
+ ppll7ctrl0_val |= (u32)(postdiv2 << 23 | postdiv1 << 20 | fbdiv << 8 | refdiv << 2);
ppll7ctrl0_val &= MIDIA_PPLL7_FREQ_DEVIDER_MASK;
ppll7ctrl0 |= ppll7ctrl0_val;

@@ -219,47 +229,30 @@ int hdmi_pxl_ppll7_init(struct dss_hw_ctx *ctx, uint64_t pixel_clock)
ppll7ctrl1 &= ~MIDIA_PPLL7_FRAC_MODE_MASK;

ppll7ctrl1_val = 0x0;
- ppll7ctrl1_val |= (uint32_t)(1 << 25 | 0 << 24 | frac);
+ ppll7ctrl1_val |= (u32)(1 << 25 | 0 << 24 | frac);
ppll7ctrl1_val &= MIDIA_PPLL7_FRAC_MODE_MASK;
ppll7ctrl1 |= ppll7ctrl1_val;

outp32(ctx->pmctrl_base + MIDIA_PPLL7_CTRL1, ppll7ctrl1);

-#if 1
- ret = clk_set_rate(ctx->dss_pxl0_clk, 144000000UL);
-#else
- /*comfirm ldi1 switch ppll7*/
- if (pixel_clock_ori <= 255000000)
- ret = clk_set_rate(ctx->dss_pxl0_clk, DEFAULT_MIDIA_PPLL7_CLOCK_FREQ/7);
- else if (pixel_clock_ori <= 415000000)
- ret = clk_set_rate(ctx->dss_pxl0_clk, DEFAULT_MIDIA_PPLL7_CLOCK_FREQ/5);
- else if (pixel_clock_ori <= 594000000)
- ret = clk_set_rate(ctx->dss_pxl0_clk, DEFAULT_MIDIA_PPLL7_CLOCK_FREQ/3);
- else {
- DRM_ERROR("Clock don't support!!\n");
- return -EINVAL;
- }
-#endif
+ DRM_INFO("PLL7 set to (0x%0x, 0x%0x)\n", ppll7ctrl0, ppll7ctrl1);
+
+ ret = clk_set_rate(ctx->dss_pxl0_clk, dss_pxl0_clk);
+ if (ret < 0)
+ DRM_ERROR("%s: clk_set_rate(dss_pxl0_clk, %llu) failed: %d!\n",
+ __func__, dss_pxl0_clk, ret);
+ else
+ DRM_INFO("dss_pxl0_clk:[%llu]->[%lu].\n",
+ dss_pxl0_clk, clk_get_rate(ctx->dss_pxl0_clk));

- if (ret < 0) {
- DRM_ERROR("dss_pxl0_clk clk_set_rate(%llu) failed, error=%d!\n",
- pixel_clock_cur, ret);
- }
return ret;
}

-/*******************************************************************************
- **
- */
-static void dss_ldi_set_mode(struct dss_crtc *acrtc)
+static u64 dss_calculate_clock(struct dss_crtc *acrtc,
+ const struct drm_display_mode *mode)
{
- int ret;
- uint64_t clk_Hz;
- struct dss_hw_ctx *ctx = acrtc->ctx;
- struct drm_display_mode *mode = &acrtc->base.state->mode;
- struct drm_display_mode *adj_mode = &acrtc->base.state->adjusted_mode;
+ u64 clk_Hz;

- DRM_INFO("mode->clock(org) = %u\n", mode->clock);
if (acrtc->ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
if (mode->clock == 148500)
clk_Hz = 144000 * 1000UL;
@@ -275,10 +268,6 @@ static void dss_ldi_set_mode(struct dss_crtc *acrtc)
/* Adjust pixel clock for compatibility with 10.1 inch special displays. */
if (mode->clock == 148500 && mode->width_mm == 532 && mode->height_mm == 299)
clk_Hz = 152000 * 1000UL;
-
- DRM_INFO("HDMI real need clock = %llu \n", clk_Hz);
- hdmi_pxl_ppll7_init(ctx, clk_Hz);
- adj_mode->clock = clk_Hz / 1000;
} else {
if (mode->clock == 148500)
clk_Hz = 144000 * 1000UL;
@@ -290,19 +279,40 @@ static void dss_ldi_set_mode(struct dss_crtc *acrtc)
clk_Hz = 72000 * 1000UL;
else
clk_Hz = mode->clock * 1000UL;
+ }

- /*
- * Success should be guaranteed in mode_valid call back,
- * so failure shouldn't happen here
- */
+ return clk_Hz;
+}
+
+static void dss_ldi_set_mode(struct dss_crtc *acrtc)
+{
+ struct drm_display_mode *adj_mode = &acrtc->base.state->adjusted_mode;
+ struct drm_display_mode *mode = &acrtc->base.state->mode;
+ struct dss_hw_ctx *ctx = acrtc->ctx;
+ u32 clock = mode->clock;
+ u64 clk_Hz;
+ int ret;
+
+ clk_Hz = dss_calculate_clock(acrtc, mode);
+
+ DRM_INFO("Requested clock %u kHz, setting to %llu kHz\n",
+ clock, clk_Hz / 1000);
+
+ if (acrtc->ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
+ ret = hdmi_pxl_ppll7_init(ctx, clk_Hz);
+ } else {
ret = clk_set_rate(ctx->dss_pxl0_clk, clk_Hz);
- if (ret) {
- DRM_ERROR("failed to set pixel clk %llu Hz (%d)\n", clk_Hz, ret);
+ if (!ret) {
+ clk_Hz = clk_get_rate(ctx->dss_pxl0_clk);
+ DRM_INFO("dss_pxl0_clk:[%llu]->[%lu].\n",
+ clk_Hz, clk_get_rate(ctx->dss_pxl0_clk));
}
- adj_mode->clock = clk_get_rate(ctx->dss_pxl0_clk) / 1000;
}

- DRM_INFO("dss_pxl0_clk [%llu]->[%lu] \n", clk_Hz, clk_get_rate(ctx->dss_pxl0_clk));
+ if (ret)
+ DRM_ERROR("failed to set pixel clock\n");
+ else
+ adj_mode->clock = clk_Hz / 1000;

dpe_init(acrtc);
}
@@ -460,6 +470,75 @@ static irqreturn_t dss_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}

+static bool dss_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct dss_crtc *acrtc = to_dss_crtc(crtc);
+ struct dss_hw_ctx *ctx = acrtc->ctx;
+ u64 clk_Hz;
+
+ /* Check if clock is too high */
+ if (mode->clock > 594000)
+ return false;
+ /*
+ * FIXME: the code should, instead, do some calculus instead of
+ * hardcoding the modes. Clearly, there's something missing at
+ * dss_calculate_clock()
+ */
+
+#if 0
+ /*
+ * HACK: reports at Hikey 970 mailing lists with the downstream
+ * Official Linaro 4.9 driver seem to indicate that some monitor
+ * modes aren't properly set. There, this hack was added.
+ *
+ * On my monitors, this wasn't needed, but better to keep this
+ * code here, together with this notice, just in case.
+ */
+ if ((mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 148500)
+ || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 148352)
+ || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 80192)
+ || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 74250)
+ || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 61855)
+ || (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 147116)
+ || (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 146250)
+ || (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 144589)
+ || (mode->hdisplay == 1600 && mode->vdisplay == 1200 && mode->clock == 160961)
+ || (mode->hdisplay == 1600 && mode->vdisplay == 900 && mode->clock == 118963)
+ || (mode->hdisplay == 1440 && mode->vdisplay == 900 && mode->clock == 126991)
+ || (mode->hdisplay == 1280 && mode->vdisplay == 1024 && mode->clock == 128946)
+ || (mode->hdisplay == 1280 && mode->vdisplay == 1024 && mode->clock == 98619)
+ || (mode->hdisplay == 1280 && mode->vdisplay == 960 && mode->clock == 102081)
+ || (mode->hdisplay == 1280 && mode->vdisplay == 800 && mode->clock == 83496)
+ || (mode->hdisplay == 1280 && mode->vdisplay == 720 && mode->clock == 74440)
+ || (mode->hdisplay == 1280 && mode->vdisplay == 720 && mode->clock == 74250)
+ || (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 78800)
+ || (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 75000)
+ || (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 81833)
+ || (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 48907)
+ || (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000)
+ || (mode->hdisplay == 800 && mode->vdisplay == 480 && mode->clock == 32000)
+ )
+#endif
+ {
+ clk_Hz = dss_calculate_clock(acrtc, mode);
+
+ /*
+ * On Kirin970, PXL0 clock is set to a const value,
+ * independently of the pixel clock.
+ */
+ if (acrtc->ctx->g_dss_version_tag != FB_ACCEL_KIRIN970)
+ clk_Hz = clk_round_rate(ctx->dss_pxl0_clk, mode->clock * 1000);
+
+ adj_mode->clock = clk_Hz / 1000;
+
+ return true;
+ }
+
+ return false;
+}
+
static void dss_crtc_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
@@ -533,6 +612,7 @@ static void dss_crtc_atomic_flush(struct drm_crtc *crtc,
}

static const struct drm_crtc_helper_funcs dss_crtc_helper_funcs = {
+ .mode_fixup = dss_crtc_mode_fixup,
.atomic_enable = dss_crtc_enable,
.atomic_disable = dss_crtc_disable,
.mode_set_nofb = dss_crtc_mode_set_nofb,
diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c b/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
index 99be8dfe05e6..f7f0deca3f53 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
@@ -1457,6 +1457,39 @@ static void dsi_encoder_enable(struct drm_encoder *encoder)
dsi->enable = true;
}

+static enum drm_mode_status dsi_encoder_mode_valid(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode)
+
+{
+ const struct drm_crtc_helper_funcs *crtc_funcs;
+ struct drm_display_mode adj_mode;
+ int clock = mode->clock;
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, encoder->dev) {
+ drm_mode_copy(&adj_mode, mode);
+
+ crtc_funcs = crtc->helper_private;
+ if (crtc_funcs && crtc_funcs->mode_fixup) {
+ if (!crtc_funcs->mode_fixup(crtc, mode, &adj_mode)) {
+ DRM_INFO("Discarded mode: %ix%i@%i, clock: %i (adjusted to %i)",
+ mode->hdisplay, mode->vdisplay,
+ drm_mode_vrefresh(mode),
+ mode->clock, clock);
+
+ return MODE_BAD;
+ }
+ clock = adj_mode.clock;
+ }
+ }
+
+ DRM_INFO("Valid mode: %ix%i@%i, clock %i (adjusted to %i)",
+ mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
+ mode->clock, clock);
+
+ return MODE_OK;
+}
+
static void dsi_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
@@ -1476,6 +1509,7 @@ static int dsi_encoder_atomic_check(struct drm_encoder *encoder,

static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = {
.atomic_check = dsi_encoder_atomic_check,
+ .mode_valid = dsi_encoder_mode_valid,
.mode_set = dsi_encoder_mode_set,
.enable = dsi_encoder_enable,
.disable = dsi_encoder_disable
--
2.26.2