diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index f77dcfaade6c..b4c7af3ab6ae 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -603,6 +603,72 @@ static void dsi_encoder_enable(struct drm_encoder *encoder) dsi->enable = true; } +static enum drm_mode_status dsi_encoder_phy_mode_valid( + struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + struct mipi_phy_params phy; + u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + u32 req_kHz, act_kHz, lane_byte_clk_kHz; + + /* Calculate the lane byte clk using the adjusted mode clk */ + memset(&phy, 0, sizeof(phy)); + req_kHz = mode->clock * bpp / dsi->lanes; + act_kHz = dsi_calc_phy_rate(req_kHz, &phy); + lane_byte_clk_kHz = act_kHz / 8; + + DRM_DEBUG_DRIVER("Checking mode %ix%i-%i@%i clock: %i...", + mode->hdisplay, mode->vdisplay, bpp, + drm_mode_vrefresh(mode), mode->clock); + + /* + * Make sure the adjusted mode clock and the lane byte clk + * have a common denominator base frequency + */ + if (mode->clock/dsi->lanes == lane_byte_clk_kHz/3) { + DRM_DEBUG_DRIVER("OK!\n"); + return MODE_OK; + } + + DRM_DEBUG_DRIVER("BAD!\n"); + return MODE_BAD; +} + +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 = NULL; + struct drm_crtc *crtc = NULL; + struct drm_display_mode adj_mode; + enum drm_mode_status ret; + + /* + * The crtc might adjust the mode, so go through the + * possible crtcs (technically just one) and call + * mode_fixup to figure out the adjusted mode before we + * validate it. + */ + drm_for_each_crtc(crtc, encoder->dev) { + /* + * reset adj_mode to the mode value each time, + * so we don't adjust the mode twice + */ + 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)) + return MODE_BAD; + + ret = dsi_encoder_phy_mode_valid(encoder, &adj_mode); + if (ret != MODE_OK) + return ret; + } + return MODE_OK; +} + static void dsi_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) @@ -622,6 +688,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 diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index 39f7d15673ed..9823477b1855 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -178,6 +178,19 @@ static void ade_init(struct ade_hw_ctx *ctx) FRM_END_START_MASK, REG_EFFECTIVE_IN_ADEEN_FRMEND); } +static bool ade_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct ade_crtc *acrtc = to_ade_crtc(crtc); + struct ade_hw_ctx *ctx = acrtc->ctx; + + adjusted_mode->clock = + clk_round_rate(ctx->ade_pix_clk, mode->clock * 1000) / 1000; + return true; +} + + static void ade_set_pix_clk(struct ade_hw_ctx *ctx, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) @@ -555,6 +568,7 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc, } static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = { + .mode_fixup = ade_crtc_mode_fixup, .mode_set_nofb = ade_crtc_mode_set_nofb, .atomic_begin = ade_crtc_atomic_begin, .atomic_flush = ade_crtc_atomic_flush,