kernel_samsung_a34x-permissive/drivers/gpu/drm/mediatek/mtk_drm_lowpower.c

671 lines
18 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <drm/mediatek_drm.h>
#include "mtk_drm_lowpower.h"
#include "mtk_drm_crtc.h"
#include "mtk_drm_drv.h"
#include "mtk_drm_ddp.h"
#include "mtk_drm_ddp_comp.h"
#include "mtk_drm_mmp.h"
#include "mtk_drm_trace.h"
#define MAX_ENTER_IDLE_RSZ_RATIO 300
static void mtk_drm_idlemgr_enable_crtc(struct drm_crtc *crtc);
static void mtk_drm_idlemgr_disable_crtc(struct drm_crtc *crtc);
static void mtk_drm_vdo_mode_enter_idle(struct drm_crtc *crtc)
{
struct mtk_drm_private *priv = crtc->dev->dev_private;
struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
int i, j;
struct cmdq_pkt *handle;
struct cmdq_client *client = mtk_crtc->gce_obj.client[CLIENT_CFG];
struct mtk_ddp_comp *comp;
mtk_crtc_pkt_create(&handle, crtc, client);
if (mtk_drm_helper_get_opt(priv->helper_opt,
MTK_DRM_OPT_IDLEMGR_BY_REPAINT) &&
atomic_read(&state->plane_enabled_num) > 1) {
atomic_set(&priv->idle_need_repaint, 1);
drm_trigger_repaint(DRM_REPAINT_FOR_IDLE, crtc->dev);
}
if (mtk_drm_helper_get_opt(priv->helper_opt,
MTK_DRM_OPT_IDLEMGR_DISABLE_ROUTINE_IRQ)) {
mtk_disp_mutex_inten_disable_cmdq(mtk_crtc->mutex[0], handle);
for_each_comp_in_cur_crtc_path(comp, mtk_crtc, i, j)
mtk_ddp_comp_io_cmd(comp, handle, IRQ_LEVEL_IDLE, NULL);
}
comp = mtk_ddp_comp_request_output(mtk_crtc);
if (comp) {
mtk_ddp_comp_io_cmd(comp, handle, DSI_VFP_IDLE_MODE, NULL);
if (mtk_drm_helper_get_opt(priv->helper_opt, MTK_DRM_OPT_LFR)) {
int en = 0;
mtk_ddp_comp_io_cmd(comp, handle, DSI_LFR_SET, &en);
}
}
cmdq_pkt_flush(handle);
cmdq_pkt_destroy(handle);
drm_crtc_vblank_off(crtc);
}
static void mtk_drm_cmd_mode_enter_idle(struct drm_crtc *crtc)
{
mtk_drm_idlemgr_disable_crtc(crtc);
lcm_fps_ctx_reset(crtc);
}
static void mtk_drm_vdo_mode_leave_idle(struct drm_crtc *crtc)
{
struct mtk_drm_private *priv = crtc->dev->dev_private;
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
int i, j;
struct cmdq_pkt *handle;
struct cmdq_client *client = mtk_crtc->gce_obj.client[CLIENT_CFG];
struct mtk_ddp_comp *comp;
mtk_crtc_pkt_create(&handle, crtc, client);
if (mtk_drm_helper_get_opt(priv->helper_opt,
MTK_DRM_OPT_IDLEMGR_DISABLE_ROUTINE_IRQ)) {
mtk_disp_mutex_inten_enable_cmdq(mtk_crtc->mutex[0], handle);
for_each_comp_in_cur_crtc_path(comp, mtk_crtc, i, j)
mtk_ddp_comp_io_cmd(comp, handle, IRQ_LEVEL_ALL, NULL);
}
comp = mtk_ddp_comp_request_output(mtk_crtc);
if (comp) {
mtk_ddp_comp_io_cmd(comp, handle, DSI_VFP_DEFAULT_MODE, NULL);
if (mtk_drm_helper_get_opt(priv->helper_opt, MTK_DRM_OPT_LFR)) {
int en = 1;
mtk_ddp_comp_io_cmd(comp, handle, DSI_LFR_SET, &en);
}
}
cmdq_pkt_flush(handle);
cmdq_pkt_destroy(handle);
drm_crtc_vblank_on(crtc);
}
static void mtk_drm_cmd_mode_leave_idle(struct drm_crtc *crtc)
{
mtk_drm_idlemgr_enable_crtc(crtc);
lcm_fps_ctx_reset(crtc);
}
static void mtk_drm_idlemgr_enter_idle_nolock(struct drm_crtc *crtc)
{
struct mtk_drm_private *priv = crtc->dev->dev_private;
struct mtk_ddp_comp *output_comp;
int index = drm_crtc_index(crtc);
bool mode;
output_comp = priv->ddp_comp[DDP_COMPONENT_DSI0];
if (!output_comp)
return;
mode = mtk_dsi_is_cmd_mode(output_comp);
CRTC_MMP_EVENT_START(index, enter_idle, mode, 0);
mtk_drm_trace_c("%d|DISP:idle_enter|%d",
hwc_pid, 1);
if (mode)
mtk_drm_cmd_mode_enter_idle(crtc);
else
mtk_drm_vdo_mode_enter_idle(crtc);
mtk_drm_trace_c("%d|DISP:idle_enter|%d",
hwc_pid, 0);
CRTC_MMP_EVENT_END(index, enter_idle, mode, 0);
}
static void mtk_drm_idlemgr_leave_idle_nolock(struct drm_crtc *crtc)
{
struct mtk_drm_private *priv = crtc->dev->dev_private;
struct mtk_ddp_comp *output_comp;
int index = drm_crtc_index(crtc);
bool mode;
output_comp = priv->ddp_comp[DDP_COMPONENT_DSI0];
if (!output_comp)
return;
mode = mtk_dsi_is_cmd_mode(output_comp);
CRTC_MMP_EVENT_START(index, leave_idle, mode, 0);
mtk_drm_trace_c("%d|DISP:idle_leave|%d",
hwc_pid, 1);
if (mode)
mtk_drm_cmd_mode_leave_idle(crtc);
else
mtk_drm_vdo_mode_leave_idle(crtc);
mtk_drm_trace_c("%d|DISP:idle_leave|%d",
hwc_pid, 0);
CRTC_MMP_EVENT_END(index, leave_idle, mode, 0);
}
bool mtk_drm_is_idle(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_drm_idlemgr *idlemgr = mtk_crtc->idlemgr;
if (!idlemgr)
return false;
return idlemgr->idlemgr_ctx->is_idle;
}
void mtk_drm_idlemgr_kick_async(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = NULL;
struct mtk_drm_idlemgr *idlemgr;
if (crtc)
mtk_crtc = to_mtk_crtc(crtc);
if (mtk_crtc && mtk_crtc->idlemgr)
idlemgr = mtk_crtc->idlemgr;
else
return;
atomic_set(&idlemgr->kick_task_active, 1);
wake_up_interruptible(&idlemgr->kick_wq);
}
void mtk_drm_idlemgr_kick(const char *source, struct drm_crtc *crtc,
int need_lock)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_drm_idlemgr *idlemgr;
struct mtk_drm_idlemgr_context *idlemgr_ctx;
if (!mtk_crtc->idlemgr)
return;
idlemgr = mtk_crtc->idlemgr;
idlemgr_ctx = idlemgr->idlemgr_ctx;
/* get lock to protect idlemgr_last_kick_time and is_idle */
if (need_lock)
DDP_MUTEX_LOCK(&mtk_crtc->lock, __func__, __LINE__);
/* update kick timestamp */
idlemgr_ctx->idlemgr_last_kick_time = sched_clock();
idlemgr_ctx->idle_vblank_check_internal = 0;
if (idlemgr_ctx->is_idle) {
DDPINFO("[LP] kick idle from [%s]\n", source);
if (mtk_crtc->esd_ctx)
atomic_set(&mtk_crtc->esd_ctx->target_time, 0);
mtk_drm_idlemgr_leave_idle_nolock(crtc);
idlemgr_ctx->is_idle = 0;
/* wake up idlemgr process to monitor next idle state */
wake_up_interruptible(&idlemgr->idlemgr_wq);
}
if (need_lock)
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
}
unsigned int mtk_drm_set_idlemgr(struct drm_crtc *crtc, unsigned int flag,
bool need_lock)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_drm_idlemgr *idlemgr = mtk_crtc->idlemgr;
unsigned int old_flag;
if (!idlemgr)
return 0;
old_flag = atomic_read(&idlemgr->idlemgr_task_active);
if (flag) {
DDPINFO("[LP] enable idlemgr\n");
atomic_set(&idlemgr->idlemgr_task_active, 1);
wake_up_interruptible(&idlemgr->idlemgr_wq);
} else {
DDPINFO("[LP] disable idlemgr\n");
atomic_set(&idlemgr->idlemgr_task_active, 0);
mtk_drm_idlemgr_kick(__func__, crtc, need_lock);
}
return old_flag;
}
unsigned long long
mtk_drm_set_idle_check_interval(struct drm_crtc *crtc,
unsigned long long new_interval)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
unsigned long long old_interval = 0;
if (!(mtk_crtc && mtk_crtc->idlemgr && mtk_crtc->idlemgr->idlemgr_ctx))
return 0;
old_interval = mtk_crtc->idlemgr->idlemgr_ctx->idle_check_interval;
mtk_crtc->idlemgr->idlemgr_ctx->idle_check_interval = new_interval;
return old_interval;
}
unsigned long long
mtk_drm_get_idle_check_interval(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
if (!(mtk_crtc && mtk_crtc->idlemgr && mtk_crtc->idlemgr->idlemgr_ctx))
return 0;
return mtk_crtc->idlemgr->idlemgr_ctx->idle_check_interval;
}
static int mtk_drm_idlemgr_get_rsz_ratio(struct mtk_crtc_state *state)
{
int src_w = state->rsz_src_roi.width;
int src_h = state->rsz_src_roi.height;
int dst_w = state->rsz_dst_roi.width;
int dst_h = state->rsz_dst_roi.height;
int ratio_w, ratio_h;
if (src_w == 0 || src_h == 0)
return 100;
ratio_w = dst_w * 100 / src_w;
ratio_h = dst_h * 100 / src_h;
return ((ratio_w > ratio_h) ? ratio_w : ratio_h);
}
static bool is_yuv(uint32_t format)
{
switch (format) {
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
return true;
default:
break;
}
return false;
}
static bool mtk_planes_is_yuv_fmt(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
int i;
for (i = 0; i < mtk_crtc->layer_nr; i++) {
struct drm_plane *plane = &mtk_crtc->planes[i].base;
struct mtk_plane_state *plane_state =
to_mtk_plane_state(plane->state);
struct mtk_plane_pending_state *pending = &plane_state->pending;
unsigned int fmt = pending->format;
if (pending->enable && is_yuv(fmt))
return true;
}
return false;
}
static int mtk_drm_async_kick_idlemgr_thread(void *data)
{
struct drm_crtc *crtc = (struct drm_crtc *)data;
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_drm_idlemgr *idlemgr = mtk_crtc->idlemgr;
int ret = 0;
while (!kthread_should_stop()) {
ret = wait_event_interruptible(
idlemgr->kick_wq,
atomic_read(&idlemgr->kick_task_active));
atomic_set(&idlemgr->kick_task_active, 0);
mtk_drm_idlemgr_kick(__func__, crtc, true);
}
return 0;
}
static int mtk_drm_idlemgr_monitor_thread(void *data)
{
int ret = 0;
unsigned long long t_to_check = 0;
unsigned long long t_idle;
struct drm_crtc *crtc = (struct drm_crtc *)data;
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_drm_idlemgr *idlemgr = mtk_crtc->idlemgr;
struct mtk_drm_idlemgr_context *idlemgr_ctx = idlemgr->idlemgr_ctx;
struct mtk_drm_private *priv = crtc->dev->dev_private;
struct mtk_crtc_state *mtk_state = NULL;
struct drm_vblank_crtc *vblank = NULL;
int crtc_id = drm_crtc_index(crtc);
msleep(50000);
do {
ret = wait_event_interruptible(
idlemgr->idlemgr_wq,
atomic_read(&idlemgr->idlemgr_task_active));
if (ret < 0)
DDPMSG("wait %s fail, ret=%d\n", __func__, ret);
t_idle = local_clock() - idlemgr_ctx->idlemgr_last_kick_time;
if (idlemgr_ctx->idle_vblank_check_internal)
t_to_check = idlemgr_ctx->idle_vblank_check_internal *
1000 * 1000 - t_idle;
else
t_to_check = idlemgr_ctx->idle_check_interval *
1000 * 1000 - t_idle;
do_div(t_to_check, 1000000);
t_to_check = min(t_to_check, 1000ULL);
/* when starting up before the first time kick */
if (idlemgr_ctx->idlemgr_last_kick_time == 0)
msleep_interruptible(idlemgr_ctx->idle_check_interval);
else if (t_to_check > 0)
msleep_interruptible(t_to_check);
DDP_MUTEX_LOCK(&mtk_crtc->lock, __func__, __LINE__);
if (!mtk_crtc->enabled) {
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
mtk_crtc_wait_status(crtc, 1, MAX_SCHEDULE_TIMEOUT);
continue;
}
if (mtk_crtc_is_frame_trigger_mode(crtc) &&
atomic_read(&priv->crtc_rel_present[crtc_id]) <
atomic_read(&priv->crtc_present[crtc_id])) {
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
continue;
}
if (crtc->state) {
mtk_state = to_mtk_crtc_state(crtc->state);
if (mtk_state->prop_val[CRTC_PROP_DOZE_ACTIVE]) {
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__,
__LINE__);
continue;
}
if ((bool)mtk_state->prop_val[CRTC_PROP_HBM_ENABLE]) {
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__,
__LINE__);
continue;
}
/* do not enter VDO idle when rsz ratio >= 2.5;
* And When layer fmt is YUV in VP scenario, it
* will flicker into idle repaint, so let it not
* into idle repaint as workaround.
*/
if (mtk_crtc_is_frame_trigger_mode(crtc) == 0 &&
((mtk_drm_idlemgr_get_rsz_ratio(mtk_state) >=
MAX_ENTER_IDLE_RSZ_RATIO) ||
mtk_planes_is_yuv_fmt(crtc))) {
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__,
__LINE__);
continue;
}
}
if (idlemgr_ctx->is_idle
|| mtk_crtc_is_dc_mode(crtc)
|| priv->session_mode != MTK_DRM_SESSION_DL
|| mtk_crtc->sec_on) {
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
continue;
}
t_idle = local_clock() - idlemgr_ctx->idlemgr_last_kick_time;
if ((idlemgr_ctx->idle_vblank_check_internal &&
t_idle < idlemgr_ctx->idle_vblank_check_internal * 1000 * 1000) ||
(!idlemgr_ctx->idle_vblank_check_internal &&
t_idle < idlemgr_ctx->idle_check_interval * 1000 * 1000)) {
/* kicked in idle_check_interval msec, it's not idle */
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
continue;
}
/* double check if dynamic switch on/off */
if (atomic_read(&idlemgr->idlemgr_task_active)) {
DDPINFO("[LP] enter idle\n");
crtc_id = drm_crtc_index(crtc);
vblank = &crtc->dev->vblank[crtc_id];
/* enter idle state */
if (!vblank || atomic_read(&vblank->refcount) == 0) {
mtk_drm_idlemgr_enter_idle_nolock(crtc);
idlemgr_ctx->is_idle = 1;
idlemgr_ctx->idle_vblank_check_internal = 0;
if (mtk_crtc->esd_ctx) {
atomic_set(&mtk_crtc->esd_ctx->target_time, 1);
wake_up_interruptible(
&mtk_crtc->esd_ctx->check_task_wq);
}
} else {
idlemgr_ctx->idlemgr_last_kick_time =
sched_clock();
idlemgr_ctx->idle_vblank_check_internal = 17;
}
}
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
wait_event_interruptible(idlemgr->idlemgr_wq,
!idlemgr_ctx->is_idle);
} while (!kthread_should_stop());
return 0;
}
int mtk_drm_idlemgr_init(struct drm_crtc *crtc, int index)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_drm_idlemgr *idlemgr =
kzalloc(sizeof(struct mtk_drm_idlemgr), GFP_KERNEL);
struct mtk_drm_idlemgr_context *idlemgr_ctx =
kzalloc(sizeof(struct mtk_drm_idlemgr_context), GFP_KERNEL);
const int len = 50;
char name[len];
if (!idlemgr) {
DDPPR_ERR("struct mtk_drm_idlemgr allocate fail\n");
kfree(idlemgr_ctx);
return -ENOMEM;
}
if (!idlemgr_ctx) {
DDPPR_ERR("struct mtk_drm_idlemgr_context allocate fail\n");
kfree(idlemgr);
return -ENOMEM;
}
idlemgr->idlemgr_ctx = idlemgr_ctx;
mtk_crtc->idlemgr = idlemgr;
idlemgr_ctx->session_mode_before_enter_idle = MTK_DRM_SESSION_INVALID;
idlemgr_ctx->is_idle = 0;
idlemgr_ctx->enterulps = 0;
idlemgr_ctx->idlemgr_last_kick_time = ~(0ULL);
idlemgr_ctx->cur_lp_cust_mode = 0;
idlemgr_ctx->idle_check_interval = 50;
idlemgr_ctx->idle_vblank_check_internal = 0;
snprintf(name, len, "mtk_drm_disp_idlemgr-%d", index);
idlemgr->idlemgr_task =
kthread_create(mtk_drm_idlemgr_monitor_thread, crtc, name);
init_waitqueue_head(&idlemgr->idlemgr_wq);
atomic_set(&idlemgr->idlemgr_task_active, 1);
wake_up_process(idlemgr->idlemgr_task);
snprintf(name, len, "dis_ki-%d", index);
idlemgr->kick_task =
kthread_create(mtk_drm_async_kick_idlemgr_thread, crtc, name);
init_waitqueue_head(&idlemgr->kick_wq);
atomic_set(&idlemgr->kick_task_active, 0);
wake_up_process(idlemgr->kick_task);
return 0;
}
static void mtk_drm_idlemgr_disable_connector(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_ddp_comp *output_comp;
output_comp = mtk_ddp_comp_request_output(mtk_crtc);
if (output_comp)
mtk_ddp_comp_io_cmd(output_comp, NULL, CONNECTOR_DISABLE, NULL);
}
static void mtk_drm_idlemgr_enable_connector(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_ddp_comp *output_comp;
output_comp = mtk_ddp_comp_request_output(mtk_crtc);
if (output_comp)
mtk_ddp_comp_io_cmd(output_comp, NULL, CONNECTOR_ENABLE, NULL);
}
static void mtk_drm_idlemgr_disable_crtc(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
unsigned int crtc_id = drm_crtc_index(&mtk_crtc->base);
bool mode = mtk_crtc_is_dc_mode(crtc);
DDPINFO("%s, crtc%d+\n", __func__, crtc_id);
if (mode) {
DDPINFO("crtc%d mode:%d bypass enter idle\n", crtc_id, mode);
DDPINFO("crtc%d do %s-\n", crtc_id, __func__);
return;
}
/* 1. stop CRTC */
mtk_crtc_stop(mtk_crtc, true);
/* 2. disconnect addon module and recover config */
mtk_crtc_disconnect_addon_module(crtc);
/* 3. set HRT BW to 0 */
#ifdef MTK_FB_MMDVFS_SUPPORT
mtk_disp_set_hrt_bw(mtk_crtc, 0);
#endif
/* 4. disconnect path */
mtk_crtc_disconnect_default_path(mtk_crtc);
/* 5. power off all modules in this CRTC */
mtk_crtc_ddp_unprepare(mtk_crtc);
mtk_drm_idlemgr_disable_connector(crtc);
drm_crtc_vblank_off(crtc);
mtk_crtc_vblank_irq(&mtk_crtc->base);
/* 6. power off MTCMOS */
mtk_drm_top_clk_disable_unprepare(crtc->dev);
/* 7. disable fake vsync if need */
mtk_drm_fake_vsync_switch(crtc, false);
DDPINFO("crtc%d do %s-\n", crtc_id, __func__);
}
/* TODO: we should restore the current setting rather than default setting */
static void mtk_drm_idlemgr_enable_crtc(struct drm_crtc *crtc)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
unsigned int crtc_id = drm_crtc_index(crtc);
bool mode = mtk_crtc_is_dc_mode(crtc);
struct mtk_ddp_comp *comp;
unsigned int i, j;
DDPINFO("crtc%d do %s+\n", crtc_id, __func__);
if (mode) {
DDPINFO("crtc%d mode:%d bypass exit idle\n", crtc_id, mode);
DDPINFO("crtc%d do %s-\n", crtc_id, __func__);
return;
}
/* 1. power on mtcmos */
mtk_drm_top_clk_prepare_enable(crtc->dev);
/* 2. prepare modules would be used in this CRTC */
mtk_drm_idlemgr_enable_connector(crtc);
mtk_crtc_ddp_prepare(mtk_crtc);
/* 3. start trigger loop first to keep gce alive */
if (crtc_id == 0) {
#if defined(CONFIG_MACH_MT6873) || defined(CONFIG_MACH_MT6853) \
|| defined(CONFIG_MACH_MT6833)
if (!mtk_crtc_is_frame_trigger_mode(crtc))
mtk_crtc_start_sodi_loop(crtc);
#endif
mtk_crtc_start_trig_loop(crtc);
mtk_crtc_hw_block_ready(crtc);
}
/* 4. connect path */
mtk_crtc_connect_default_path(mtk_crtc);
/* 5. config ddp engine & set dirty for cmd mode */
mtk_crtc_config_default_path(mtk_crtc);
/* 6. conect addon module and config */
mtk_crtc_connect_addon_module(crtc);
/* 7. restore OVL setting */
mtk_crtc_restore_plane_setting(mtk_crtc);
/* 8. Set QOS BW */
for_each_comp_in_cur_crtc_path(comp, mtk_crtc, i, j)
mtk_ddp_comp_io_cmd(comp, NULL, PMQOS_SET_BW, NULL);
/* 9. restore HRT BW */
#ifdef MTK_FB_MMDVFS_SUPPORT
mtk_disp_set_hrt_bw(mtk_crtc, mtk_crtc->qos_ctx->last_hrt_req);
#endif
/* 10. set vblank */
drm_crtc_vblank_on(crtc);
/* 11. enable fake vsync if need */
mtk_drm_fake_vsync_switch(crtc, true);
DDPINFO("crtc%d do %s-\n", crtc_id, __func__);
}