/* * mdp5_ctl_request() - CTL dynamic allocation * * Note: Current implementation considers that we can only have one CRTC per CTL * * @return first free CTL */ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr, struct drm_crtc *crtc) { struct mdp5_ctl *ctl = NULL; unsigned long flags; int c; spin_lock_irqsave(&ctl_mgr->pool_lock, flags); for (c = 0; c < ctl_mgr->nctl; c++) if (!ctl_mgr->ctls[c].busy) break; if (unlikely(c >= ctl_mgr->nctl)) { dev_err(ctl_mgr->dev->dev, "No more CTL available!"); goto unlock; } ctl = &ctl_mgr->ctls[c]; ctl->lm = mdp5_crtc_get_lm(crtc); ctl->crtc = crtc; ctl->busy = true; ctl->pending_ctl_trigger = 0; DBG("CTL %d allocated", ctl->id); unlock: spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); return ctl; }
static void pingpong_tearcheck_disable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0); clk_disable_unprepare(mdp5_kms->vsync_clk); }
static int pingpong_tearcheck_setup(struct drm_encoder *encoder, struct drm_display_mode *mode) { struct mdp5_kms *mdp5_kms = get_kms(encoder); struct device *dev = encoder->dev->dev; u32 total_lines_x100, vclks_line, cfg; long vsync_clk_speed; int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) { dev_err(dev, "vsync_clk is not initialized\n"); return -EINVAL; } total_lines_x100 = mode->vtotal * mode->vrefresh; if (!total_lines_x100) { dev_err(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n", __func__, mode->vtotal, mode->vrefresh); return -EINVAL; } vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE); if (vsync_clk_speed <= 0) { dev_err(dev, "vsync_clk round rate failed %ld\n", vsync_clk_speed); return -EINVAL; } vclks_line = vsync_clk_speed * 100 / total_lines_x100; cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN; cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line); mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg); mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0); mdp5_write(mdp5_kms, REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay); mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1); mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay); mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id), MDP5_PP_SYNC_THRESH_START(4) | MDP5_PP_SYNC_THRESH_CONTINUE(4)); return 0; }
static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) { struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc); struct mdp5_interface *intf = &mdp5_cmd_enc->intf; int lm = mdp5_crtc_get_lm(encoder->crtc); if (WARN_ON(!mdp5_cmd_enc->enabled)) return; /* Wait for the last frame done */ mdp_irq_wait(&mdp5_kms->base, lm2ppdone(lm)); pingpong_tearcheck_disable(encoder); mdp5_ctl_set_encoder_state(ctl, false); mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); bs_set(mdp5_cmd_enc, 0); mdp5_cmd_enc->enabled = false; }
static int pingpong_tearcheck_enable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); int ret; ret = clk_set_rate(mdp5_kms->vsync_clk, clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE)); if (ret) { dev_err(encoder->dev->dev, "vsync_clk clk_set_rate failed, %d\n", ret); return ret; } ret = clk_prepare_enable(mdp5_kms->vsync_clk); if (ret) { dev_err(encoder->dev->dev, "vsync_clk clk_prepare_enable failed, %d\n", ret); return ret; } mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1); return 0; }