diff options
Diffstat (limited to 'drivers/gpu/drm/arm/display/komeda/komeda_crtc.c')
-rw-r--r-- | drivers/gpu/drm/arm/display/komeda/komeda_crtc.c | 89 |
1 files changed, 60 insertions, 29 deletions
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index f4400788ab94..624d257da20f 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -27,8 +27,8 @@ static void komeda_crtc_update_clock_ratio(struct komeda_crtc_state *kcrtc_st) return; } - pxlclk = kcrtc_st->base.adjusted_mode.clock * 1000; - aclk = komeda_calc_aclk(kcrtc_st); + pxlclk = kcrtc_st->base.adjusted_mode.crtc_clock * 1000ULL; + aclk = komeda_crtc_get_aclk(kcrtc_st); kcrtc_st->clock_ratio = div64_u64(aclk << 32, pxlclk); } @@ -74,14 +74,6 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -unsigned long komeda_calc_aclk(struct komeda_crtc_state *kcrtc_st) -{ - struct komeda_dev *mdev = kcrtc_st->base.crtc->dev->dev_private; - unsigned long pxlclk = kcrtc_st->base.adjusted_mode.clock; - - return clk_round_rate(mdev->aclk, pxlclk * 1000); -} - /* For active a crtc, mainly need two parts of preparation * 1. adjust display operation mode. * 2. enable needed clk @@ -92,7 +84,7 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc) struct komeda_dev *mdev = kcrtc->base.dev->dev_private; struct komeda_pipeline *master = kcrtc->master; struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(kcrtc->base.state); - unsigned long pxlclk_rate = kcrtc_st->base.adjusted_mode.clock * 1000; + struct drm_display_mode *mode = &kcrtc_st->base.adjusted_mode; u32 new_mode; int err; @@ -118,7 +110,7 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc) * to enable it again. */ if (new_mode != KOMEDA_MODE_DUAL_DISP) { - err = clk_set_rate(mdev->aclk, komeda_calc_aclk(kcrtc_st)); + err = clk_set_rate(mdev->aclk, komeda_crtc_get_aclk(kcrtc_st)); if (err) DRM_ERROR("failed to set aclk.\n"); err = clk_prepare_enable(mdev->aclk); @@ -126,7 +118,7 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc) DRM_ERROR("failed to enable aclk.\n"); } - err = clk_set_rate(master->pxlclk, pxlclk_rate); + err = clk_set_rate(master->pxlclk, mode->crtc_clock * 1000); if (err) DRM_ERROR("failed to set pxlclk for pipe%d\n", master->id); err = clk_prepare_enable(master->pxlclk); @@ -342,29 +334,58 @@ komeda_crtc_atomic_flush(struct drm_crtc *crtc, komeda_crtc_do_flush(crtc, old); } +/* Returns the minimum frequency of the aclk rate (main engine clock) in Hz */ +static unsigned long +komeda_calc_min_aclk_rate(struct komeda_crtc *kcrtc, + unsigned long pxlclk) +{ + /* Once dual-link one display pipeline drives two display outputs, + * the aclk needs run on the double rate of pxlclk + */ + if (kcrtc->master->dual_link) + return pxlclk * 2; + else + return pxlclk; +} + +/* Get current aclk rate that specified by state */ +unsigned long komeda_crtc_get_aclk(struct komeda_crtc_state *kcrtc_st) +{ + struct drm_crtc *crtc = kcrtc_st->base.crtc; + struct komeda_dev *mdev = crtc->dev->dev_private; + unsigned long pxlclk = kcrtc_st->base.adjusted_mode.crtc_clock * 1000; + unsigned long min_aclk; + + min_aclk = komeda_calc_min_aclk_rate(to_kcrtc(crtc), pxlclk); + + return clk_round_rate(mdev->aclk, min_aclk); +} + static enum drm_mode_status komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m) { struct komeda_dev *mdev = crtc->dev->dev_private; struct komeda_crtc *kcrtc = to_kcrtc(crtc); struct komeda_pipeline *master = kcrtc->master; - long mode_clk, pxlclk; + unsigned long min_pxlclk, min_aclk; if (m->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; - mode_clk = m->clock * 1000; - pxlclk = clk_round_rate(master->pxlclk, mode_clk); - if (pxlclk != mode_clk) { - DRM_DEBUG_ATOMIC("pxlclk doesn't support %ld Hz\n", mode_clk); + min_pxlclk = m->clock * 1000; + if (master->dual_link) + min_pxlclk /= 2; + + if (min_pxlclk != clk_round_rate(master->pxlclk, min_pxlclk)) { + DRM_DEBUG_ATOMIC("pxlclk doesn't support %lu Hz\n", min_pxlclk); return MODE_NOCLOCK; } - /* main engine clock must be faster than pxlclk*/ - if (clk_round_rate(mdev->aclk, mode_clk) < pxlclk) { - DRM_DEBUG_ATOMIC("engine clk can't satisfy the requirement of %s-clk: %ld.\n", - m->name, pxlclk); + min_aclk = komeda_calc_min_aclk_rate(to_kcrtc(crtc), min_pxlclk); + if (clk_round_rate(mdev->aclk, min_aclk) < min_aclk) { + DRM_DEBUG_ATOMIC("engine clk can't satisfy the requirement of %s-clk: %lu.\n", + m->name, min_pxlclk); return MODE_CLOCK_HIGH; } @@ -377,10 +398,22 @@ static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode) { struct komeda_crtc *kcrtc = to_kcrtc(crtc); - struct komeda_pipeline *master = kcrtc->master; - long mode_clk = m->clock * 1000; + unsigned long clk_rate; + + drm_mode_set_crtcinfo(adjusted_mode, 0); + /* In dual link half the horizontal settings */ + if (kcrtc->master->dual_link) { + adjusted_mode->crtc_clock /= 2; + adjusted_mode->crtc_hdisplay /= 2; + adjusted_mode->crtc_hsync_start /= 2; + adjusted_mode->crtc_hsync_end /= 2; + adjusted_mode->crtc_htotal /= 2; + } - adjusted_mode->clock = clk_round_rate(master->pxlclk, mode_clk) / 1000; + clk_rate = adjusted_mode->crtc_clock * 1000; + /* crtc_clock will be used as the komeda output pixel clock */ + adjusted_mode->crtc_clock = clk_round_rate(kcrtc->master->pxlclk, + clk_rate) / 1000; return true; } @@ -488,10 +521,8 @@ int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, else sprintf(str, "None"); - DRM_INFO("crtc%d: master(pipe-%d) slave(%s) output: %s.\n", - kms->n_crtcs, master->id, str, - master->of_output_dev ? - master->of_output_dev->full_name : "None"); + DRM_INFO("CRTC-%d: master(pipe-%d) slave(%s).\n", + kms->n_crtcs, master->id, str); kms->n_crtcs++; } |