diff --git a/drivers/drm/meson_crtc.c b/drivers/drm/meson_crtc.c index 561e67eeb..8c1ff339c 100644 --- a/drivers/drm/meson_crtc.c +++ b/drivers/drm/meson_crtc.c @@ -167,6 +167,7 @@ static struct drm_crtc_state *meson_crtc_duplicate_state(struct drm_crtc *crtc) new_state->attr_changed = false; new_state->brr_update = false; new_state->brr = cur_state->brr; + new_state->seamless = false; strncpy(new_state->brr_mode, cur_state->brr_mode, DRM_DISPLAY_MODE_LEN); new_state->crtc_bgcolor_flag = cur_state->crtc_bgcolor_flag; new_state->crtc_bgcolor = cur_state->crtc_bgcolor; @@ -439,6 +440,7 @@ static void meson_crtc_atomic_print_state(struct drm_printer *p, drm_printf(p, "\t\tbrr_mode=%s\n", cstate->brr_mode); drm_printf(p, "\t\tbrr=%u\n", cstate->brr); drm_printf(p, "\t\tuboot_mode_init=%u\n", cstate->uboot_mode_init); + drm_printf(p, "\t\tseamless=%d\n", cstate->seamless); drm_printf(p, "\t\tcrtc_hdr_policy:[%u,%u]\n", cstate->crtc_hdr_process_policy, cstate->crtc_eotf_type); @@ -594,6 +596,62 @@ static bool am_meson_crtc_mode_fixup(struct drm_crtc *crtc, return true; } +static int meson_crtc_seamless_change(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + char *brr_name; + struct drm_crtc_state *new_state, *old_state; + struct drm_display_mode *new_mode, *old_mode; + struct am_meson_crtc_state *meson_crtc_state, *old_crtc_state; + + new_state = drm_atomic_get_new_crtc_state(state, crtc); + old_state = drm_atomic_get_old_crtc_state(state, crtc); + + if (!new_state || !old_state) { + DRM_INFO("%s crtc state is NULL!\n", __func__); + return 0; + } + new_mode = &new_state->adjusted_mode; + old_mode = &old_state->adjusted_mode; + meson_crtc_state = to_am_meson_crtc_state(new_state); + old_crtc_state = to_am_meson_crtc_state(old_state); + + if (new_mode->hdisplay != old_mode->hdisplay || + new_mode->vdisplay != old_mode->vdisplay || + meson_crtc_state->attr_changed || + meson_crtc_state->brr_update || + (new_mode->flags & DRM_MODE_FLAG_INTERLACE)) + return meson_crtc_state->seamless; + + if (new_state->vrr_enabled) { + if (old_state->vrr_enabled) { + /*qms->qms*/ + meson_crtc_state->seamless = true; + } else { + /*allm -> qms, new vrr_enable 1,brr_update 0*/ + brr_name = meson_crtc_state->brr_mode; + if (!strcmp(old_mode->name, brr_name)) + meson_crtc_state->seamless = true; + else + meson_crtc_state->seamless = false; + } + } else { + if (old_state->vrr_enabled) { + /*qms->allm*/ + brr_name = old_crtc_state->brr_mode; + if (!strcmp(old_mode->name, brr_name) && + !strcmp(new_mode->name, brr_name)) + meson_crtc_state->seamless = true; + else + meson_crtc_state->seamless = false; + } else { + /*none qms-> none qms*/ + meson_crtc_state->seamless = false; + } + } + + return meson_crtc_state->seamless; +} + static void am_meson_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *old_atomic_state) { @@ -708,12 +766,7 @@ static void am_meson_crtc_atomic_enable(struct drm_crtc *crtc, if (meson_crtc_state->uboot_mode_init) mode |= VMODE_INIT_BIT_MASK; - if (crtc->state->vrr_enabled && - adjusted_mode->hdisplay == old_mode->hdisplay && - adjusted_mode->vdisplay == old_mode->vdisplay && - !meson_crtc_state->attr_changed && - !meson_crtc_state->brr_update && - !(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)) { + if (meson_crtc_state->seamless) { drm_crtc_vblank_on(crtc); return; } @@ -737,7 +790,6 @@ static void am_meson_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *old_atomic_state) { struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); - struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; struct drm_crtc_state *old_crtc_state; struct drm_display_mode *old_mode; struct am_meson_crtc_state *meson_crtc_state = @@ -754,16 +806,6 @@ static void am_meson_crtc_atomic_disable(struct drm_crtc *crtc, old_mode = &old_crtc_state->adjusted_mode; drm_crtc_vblank_off(crtc); - if (crtc->state->vrr_enabled && - adjusted_mode->hdisplay == old_mode->hdisplay && - adjusted_mode->vdisplay == old_mode->vdisplay && - !meson_crtc_state->attr_changed && - !meson_crtc_state->brr_update && - !(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)) { - DRM_INFO("%s, vrr enable, skip crtc disable\n", __func__); - return; - } - if (crtc->state->event && !crtc->state->active) { spin_lock_irq(&crtc->dev->event_lock); drm_crtc_send_vblank_event(crtc, crtc->state->event); @@ -771,6 +813,11 @@ static void am_meson_crtc_atomic_disable(struct drm_crtc *crtc, crtc->state->event = NULL; } + if (meson_crtc_seamless_change(crtc, old_atomic_state)) { + DRM_INFO("skip set vmode to null\n"); + return; + } + if ((meson_crtc_state->vmode & VMODE_MASK) == VMODE_LCD && !strstr(old_mode->name, "panel")) { DRM_INFO("%s[%d], lcd skip setting null vmode\n", __func__, diff --git a/drivers/drm/meson_crtc.h b/drivers/drm/meson_crtc.h index 1b0ce8bb7..452aa4707 100644 --- a/drivers/drm/meson_crtc.h +++ b/drivers/drm/meson_crtc.h @@ -84,6 +84,8 @@ struct am_meson_crtc_state { int hdr_conversion_ctrl; bool attr_changed; bool brr_update; + + bool seamless; }; struct am_meson_crtc { diff --git a/drivers/drm/meson_hdmi.c b/drivers/drm/meson_hdmi.c index f6ad99391..e52a72c22 100644 --- a/drivers/drm/meson_hdmi.c +++ b/drivers/drm/meson_hdmi.c @@ -1535,6 +1535,11 @@ static void meson_hdmitx_cal_brr(struct am_hdmi_tx *hdmitx, if (ret < 0) { DRM_ERROR("%s, Get hdmi para by vic [%d] failed.\n", __func__, vic); + } + + if (am_hdmi_info.max_vfreq < drm_mode_vrefresh(adj_mode)) { + memset(crtc_state->brr_mode, 0, DRM_DISPLAY_MODE_LEN); + crtc_state->valid_brr = 0; } else { strncpy(crtc_state->brr_mode, para.name, DRM_DISPLAY_MODE_LEN); @@ -1542,7 +1547,8 @@ static void meson_hdmitx_cal_brr(struct am_hdmi_tx *hdmitx, crtc_state->valid_brr = 1; } - DRM_INFO("%s, %d, %d, %s\n", __func__, vic, brr, crtc_state->brr_mode); + DRM_INFO("%s, %d, %d, %s, %d\n", __func__, vic, brr, crtc_state->brr_mode, + crtc_state->valid_brr); crtc_state->brr = brr; kfree(groups); } @@ -1689,7 +1695,8 @@ int meson_encoder_vrr_change(struct drm_encoder *encoder, struct drm_crtc *crtc; struct drm_crtc_state *new_state, *old_state; struct drm_display_mode *new_mode, *old_mode; - struct am_meson_crtc_state *meson_crtc_state; + struct am_meson_crtc_state *meson_crtc_state, *old_crtc_state; + char *brr_name; connector = drm_atomic_get_new_connector_for_encoder(state, encoder); if (!connector) @@ -1702,22 +1709,54 @@ int meson_encoder_vrr_change(struct drm_encoder *encoder, crtc = conn_state->crtc; new_state = drm_atomic_get_new_crtc_state(state, crtc); old_state = drm_atomic_get_old_crtc_state(state, crtc); + + if (!new_state || !old_state) { + DRM_INFO("%s crtc state is NULL!\n", __func__); + return 0; + } new_mode = &new_state->adjusted_mode; old_mode = &old_state->adjusted_mode; meson_crtc_state = to_am_meson_crtc_state(new_state); + old_crtc_state = to_am_meson_crtc_state(old_state); - if (new_state->vrr_enabled && - new_mode->hdisplay == old_mode->hdisplay && - new_mode->vdisplay == old_mode->vdisplay && - !meson_crtc_state->attr_changed && - !meson_crtc_state->brr_update && - !(new_mode->flags & DRM_MODE_FLAG_INTERLACE)) { - DRM_INFO("[%s], vrr, skip encoder %s\n", __func__, - status == 1 ? "enable" : "disable"); - return 1; + if (new_mode->hdisplay != old_mode->hdisplay || + new_mode->vdisplay != old_mode->vdisplay || + meson_crtc_state->attr_changed || + meson_crtc_state->brr_update || + (new_mode->flags & DRM_MODE_FLAG_INTERLACE)) + return meson_crtc_state->seamless; + + if (new_state->vrr_enabled) { + if (old_state->vrr_enabled) { + /*qms->qms*/ + meson_crtc_state->seamless = true; + } else { + /*allm -> qms, new vrr_enable 1,brr_update 0*/ + brr_name = meson_crtc_state->brr_mode; + if (!strcmp(old_mode->name, brr_name)) + meson_crtc_state->seamless = true; + else + meson_crtc_state->seamless = false; + } + } else { + if (old_state->vrr_enabled) { + /*qms->allm*/ + brr_name = old_crtc_state->brr_mode; + if (!strcmp(old_mode->name, brr_name) && + !strcmp(new_mode->name, brr_name)) + meson_crtc_state->seamless = true; + else + meson_crtc_state->seamless = false; + } else { + /*none qms-> none qms*/ + meson_crtc_state->seamless = false; + } } - return 0; + if (meson_crtc_state->seamless) + DRM_INFO("[%s], vrr, skip encoder %s\n", __func__, + status == 1 ? "enable" : "disable"); + return meson_crtc_state->seamless; } void meson_hdmitx_encoder_atomic_enable(struct drm_encoder *encoder, @@ -1736,7 +1775,7 @@ void meson_hdmitx_encoder_atomic_enable(struct drm_encoder *encoder, struct am_hdmitx_connector_state *old_meson_conn_state = to_am_hdmitx_connector_state(old_conn_state); struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; - int mode_vrefresh = drm_mode_vrefresh(mode); + int dst_vrefresh, mode_vrefresh = drm_mode_vrefresh(mode); struct am_meson_crtc *amcrtc = to_am_meson_crtc(encoder->crtc); enum vmode_e vmode = meson_crtc_state->vmode; struct hdmitx_common *tx_comm = am_hdmi_info.hdmitx_dev->hdmitx_common; @@ -1748,9 +1787,10 @@ void meson_hdmitx_encoder_atomic_enable(struct drm_encoder *encoder, return; } - if (meson_encoder_vrr_change(encoder, state, 1)) { - DRM_INFO("%s, set frame rate: %d\n", __func__, mode_vrefresh); - am_hdmi_info.hdmitx_dev->set_vframe_rate_hint(mode_vrefresh * 100, NULL); + if (meson_crtc_state->seamless) { + dst_vrefresh = meson_crtc_state->base.vrr_enabled ? mode_vrefresh : 0; + DRM_INFO("%s, set frame rate: %d\n", __func__, dst_vrefresh); + am_hdmi_info.hdmitx_dev->set_vframe_rate_hint(dst_vrefresh * 100, NULL); return; } @@ -1758,17 +1798,20 @@ void meson_hdmitx_encoder_atomic_enable(struct drm_encoder *encoder, meson_conn_state->update != 1) vmode |= VMODE_INIT_BIT_MASK; - hdmitx_set_hdr_priority(am_hdmi_info.hdmitx_dev->hdmitx_common, - meson_conn_state->hdr_priority); + if (!meson_crtc_state->seamless) { + hdmitx_set_hdr_priority(am_hdmi_info.hdmitx_dev->hdmitx_common, + meson_conn_state->hdr_priority); - meson_vout_notify_mode_change(amcrtc->vout_index, - vmode, EVENT_MODE_SET_START); - meson_conn_state->hcs.mode = vmode; - hdmitx_common_do_mode_setting(am_hdmi_info.hdmitx_dev->hdmitx_common, - &meson_conn_state->hcs, &old_meson_conn_state->hcs); - meson_vout_notify_mode_change(amcrtc->vout_index, - vmode, EVENT_MODE_SET_FINISH); - meson_vout_update_mode_name(amcrtc->vout_index, mode->name, "hdmitx"); + meson_vout_notify_mode_change(amcrtc->vout_index, + vmode, EVENT_MODE_SET_START); + meson_conn_state->hcs.mode = vmode; + hdmitx_common_do_mode_setting(am_hdmi_info.hdmitx_dev->hdmitx_common, + &meson_conn_state->hcs, + &old_meson_conn_state->hcs); + meson_vout_notify_mode_change(amcrtc->vout_index, + vmode, EVENT_MODE_SET_FINISH); + meson_vout_update_mode_name(amcrtc->vout_index, mode->name, "hdmitx"); + } am_hdmi_info.hdmitx_on = 1;