diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 71dc623324b9..0ee0a030d2d1 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -102,6 +102,12 @@ enum vop2_win_dly_mode { VOP2_DLY_MODE_MAX, }; +/* + * vop2 dsc id + */ +#define ROCKCHIP_VOP2_DSC_8K 0 +#define ROCKCHIP_VOP2_DSC_4K 1 + /* * vop2 internal power domain id, * should be all none zero, 0 will be @@ -772,6 +778,12 @@ struct vop2_wb_regs { struct vop2_power_domain_data { uint8_t id; uint8_t parent_id; + /* + * @module_id_mask: module id of which module this power domain is belongs to. + * PD_CLUSTER0,1,2,3 only belongs to CLUSTER0/1/2/3, PD_Esmart0 shared by Esmart1/2/3 + */ + uint32_t module_id_mask; + const struct vop2_power_domain_regs *regs; }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 9ac8bba84366..d6700487b99d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -231,6 +231,7 @@ struct vop2_power_domain { * If the module powered by this power domain was enabled. */ bool module_on; + const struct vop2_power_domain_data *data; struct list_head list; struct delayed_work power_off_work; @@ -1435,7 +1436,8 @@ static void vop2_wait_power_domain_off(struct vop2_power_domain *pd) ret = readx_poll_timeout_atomic(vop2_power_domain_status, pd, val, !val, 0, 50 * 1000); if (ret) - DRM_DEV_ERROR(vop2->dev, "wait pd%d off timeout\n", ffs(pd->data->id) - 1); + DRM_DEV_ERROR(vop2->dev, "wait pd%d off timeout power_ctrl: 0x%x\n", + ffs(pd->data->id) - 1, vop2_readl(vop2, 0x34)); } static void vop2_wait_power_domain_on(struct vop2_power_domain *pd) @@ -1446,7 +1448,8 @@ static void vop2_wait_power_domain_on(struct vop2_power_domain *pd) ret = readx_poll_timeout_atomic(vop2_power_domain_status, pd, val, val, 0, 50 * 1000); if (ret) - DRM_DEV_ERROR(vop2->dev, "wait pd%d on timeout\n", ffs(pd->data->id) - 1); + DRM_DEV_ERROR(vop2->dev, "wait pd%d on timeout power_ctrl: 0x%x\n", + ffs(pd->data->id) - 1, vop2_readl(vop2, 0x34)); } /* @@ -1498,16 +1501,15 @@ static void vop2_power_domain_put(struct vop2_power_domain *pd) /* * For a nested power domain(PD_Cluster0 is the parent of PD_CLuster1/2/3) - * the parent powe domain must be enabled before child power domain + * the parent power domain must be enabled before child power domain * is on. * - * So we may met this condition: Cluster0 is not enabled, but PD_Cluster0 - * must enabled as one of the child PD_CLUSTER1/2/3 is enabled. + * So we may met this condition: Cluster0 is not on a activated VP, + * but PD_Cluster0 must enabled as one of the child PD_CLUSTER1/2/3 is enabled. * when all child PD is disabled, we want disable the parent - * PD(PD_CLUSTER0), but as module CLUSTER0 is not enabled, - * the turn down configuration will never take effect. - * so we will see a "wait pd0 off timeout" log when we - * turn on PD_CLUSTER0 next time. + * PD(PD_CLUSTER0), but as module CLUSTER0 is not attcthed on a activated VP, + * the turn down operation(which is take effect by vsync) will never take effect. + * so we will see a "wait pd0 off timeout" log when we turn on PD_CLUSTER0 next time. * * So don't try to turn off a power domain when the module is not * enabled. @@ -3243,18 +3245,6 @@ static void vop2_initial(struct drm_crtc *crtc) return; } - /* - * we should rest axi to clear register state - * when set pd_off_imd in vop2_power_off_all_pd, - * otherwise the bit0/1/2 of POWER_CTRL register - * will auto cleared after we clear pd_off_imd - * in the following. - */ - if (vop2->version == VOP_VERSION_RK3588) { - if (!vp->loader_protect) - vop2_clk_reset(vop2->axi_rst); - } - if (vop2_soc_is_rk3566()) VOP_CTRL_SET(vop2, otp_en, 1); @@ -3266,7 +3256,6 @@ static void vop2_initial(struct drm_crtc *crtc) else memcpy(vop2->regsbak, vop2->regs, vop2->len); - VOP_CTRL_SET(vop2, pd_off_imd, 0); VOP_MODULE_SET(vop2, wb, axi_yrgb_id, 0xd); VOP_MODULE_SET(vop2, wb, axi_uv_id, 0xe); vop2_wb_cfg_done(vp); @@ -3307,14 +3296,89 @@ static void vop2_initial(struct drm_crtc *crtc) vp->id, ret); } +/* + * The internal PD of VOP2 on rk3588 take effect immediately + * for power up and take effect by vsync for power down. + * + * And the PD_CLUSTER0 is a parent PD of PD_CLUSTER1/2/3, + * we may have this use case: + * Cluster0 is attached to VP0 for HDMI output, + * Cluster1 is attached to VP1 for MIPI DSI, + + * When we enable Cluster1 on VP1, we should enable PD_CLUSTER0 as + * it is the parent PD, event though HDMI is plugout, VP1 is disabled, + * the PD of Cluster0 should keep power on. + + * When system go to suspend: + * (1) Power down PD of Cluster1 before VP1 standby(the power down is take + * effect by vsync) + * (2) Power down PD of Cluster0 + * + * But we have problem at step (2), Cluster0 is attached to VP0. but VP0 + * is in standby mode, as it is never used or hdmi plugout. So there is + * no vsync, the power down will never take effect. + + * According to IC designer: We must power down all internal PD of VOP + * before we power down the global PD_VOP. + + * So we get this workaround: + * We we found a VP is in standby mode when we want power down a PD is + * attached to it, we release the VP from standby mode, than it will + * run a default timing and generate vsync. Than we can power down the + * PD by this vsync. After all this is done, we standby the VP at last. + */ +static void vop2_power_domain_off_by_disabled_vp(struct vop2_power_domain *pd) +{ + struct vop2_video_port *vp = NULL; + struct vop2 *vop2 = pd->vop2; + struct vop2_win *win; + struct drm_crtc *crtc; + uint32_t vp_id; + uint8_t phys_id; + int ret; + + if (pd->data->id == VOP2_PD_CLUSTER0 || pd->data->id == VOP2_PD_CLUSTER1 || + pd->data->id == VOP2_PD_CLUSTER2 || pd->data->id == VOP2_PD_CLUSTER3) { + phys_id = ffs(pd->data->module_id_mask) - 1; + win = vop2_find_win_by_phys_id(vop2, phys_id); + vp_id = ffs(win->vp_mask) - 1; + vp = &vop2->vps[vp_id]; + } else { + DRM_DEV_ERROR(vop2->dev, "unexpected power on pd%d\n", ffs(pd->data->id) - 1); + } + + if (vp) { + ret = clk_prepare_enable(vp->dclk); + if (ret < 0) + DRM_DEV_ERROR(vop2->dev, "failed to enable dclk for video port%d - %d\n", + vp->id, ret); + crtc = &vp->rockchip_crtc.crtc; + VOP_MODULE_SET(vop2, vp, aclk_en, 1); + VOP_MODULE_SET(vop2, vp, standby, 0); + vop2_power_domain_off(pd); + vop2_cfg_done(crtc); + vop2_wait_power_domain_off(pd); + + reinit_completion(&vp->dsp_hold_completion); + vop2_dsp_hold_valid_irq_enable(crtc); + VOP_MODULE_SET(vop2, vp, standby, 1); + ret = wait_for_completion_timeout(&vp->dsp_hold_completion, msecs_to_jiffies(50)); + if (!ret) + DRM_DEV_INFO(vop2->dev, "wait for vp%d dsp_hold timeout\n", vp->id); + + vop2_dsp_hold_valid_irq_disable(crtc); + VOP_MODULE_SET(vop2, vp, aclk_en, 0); + clk_disable_unprepare(vp->dclk); + } +} + static void vop2_power_off_all_pd(struct vop2 *vop2) { struct vop2_power_domain *pd, *n; - VOP_CTRL_SET(vop2, pd_off_imd, 1); list_for_each_entry_safe_reverse(pd, n, &vop2->pd_list_head, list) { - VOP_MODULE_SET(vop2, pd->data, pd, 1); - vop2_wait_power_domain_off(pd); + if (vop2_power_domain_status(pd)) + vop2_power_domain_off_by_disabled_vp(pd); pd->on = false; pd->module_on = false; } @@ -3330,7 +3394,6 @@ static void vop2_disable(struct drm_crtc *crtc) if (--vop2->enable_count > 0) return; - vop2->is_enabled = false; if (vop2->is_iommu_enabled) { /* * vop2 standby complete, so iommu detach is safe. @@ -3342,6 +3405,7 @@ static void vop2_disable(struct drm_crtc *crtc) if (vop2->version == VOP_VERSION_RK3588) vop2_power_off_all_pd(vop2); + vop2->is_enabled = false; pm_runtime_put_sync(vop2->dev); clk_disable_unprepare(vop2->pclk); diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 22bb89bc2137..7d7aa3930885 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -607,7 +607,7 @@ static const struct dsc_error_info dsc_buffer_flow[] = { static const struct vop2_dsc_data rk3588_vop_dsc_data[] = { { - .id = 0, + .id = ROCKCHIP_VOP2_DSC_8K, .pd_id = VOP2_PD_DSC_8K, .max_slice_num = 8, .max_linebuf_depth = 11, @@ -620,7 +620,7 @@ static const struct vop2_dsc_data rk3588_vop_dsc_data[] = { }, { - .id = 1, + .id = ROCKCHIP_VOP2_DSC_4K, .pd_id = VOP2_PD_DSC_4K, .max_slice_num = 2, .max_linebuf_depth = 11, @@ -2066,39 +2066,48 @@ const struct vop2_power_domain_regs rk3588_dsc_4k_pd_regs = { static const struct vop2_power_domain_data rk3588_vop_pd_data[] = { { .id = VOP2_PD_CLUSTER0, + .module_id_mask = BIT(ROCKCHIP_VOP2_CLUSTER0), .regs = &rk3588_cluster0_pd_regs, }, { .id = VOP2_PD_CLUSTER1, + .module_id_mask = BIT(ROCKCHIP_VOP2_CLUSTER1), .parent_id = VOP2_PD_CLUSTER0, .regs = &rk3588_cluster1_pd_regs, }, { .id = VOP2_PD_CLUSTER2, + .module_id_mask = BIT(ROCKCHIP_VOP2_CLUSTER2), .parent_id = VOP2_PD_CLUSTER0, .regs = &rk3588_cluster2_pd_regs, }, { .id = VOP2_PD_CLUSTER3, + .module_id_mask = BIT(ROCKCHIP_VOP2_CLUSTER3), .parent_id = VOP2_PD_CLUSTER0, .regs = &rk3588_cluster3_pd_regs, }, { .id = VOP2_PD_ESMART, + .module_id_mask = BIT(ROCKCHIP_VOP2_ESMART1) | + BIT(ROCKCHIP_VOP2_ESMART2) | + BIT(ROCKCHIP_VOP2_ESMART3), .regs = &rk3588_esmart_pd_regs, }, { .id = VOP2_PD_DSC_8K, + .module_id_mask = BIT(ROCKCHIP_VOP2_DSC_8K), .regs = &rk3588_dsc_8k_pd_regs, }, { .id = VOP2_PD_DSC_4K, + .module_id_mask = BIT(ROCKCHIP_VOP2_DSC_4K), .regs = &rk3588_dsc_4k_pd_regs, }, };