diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_vop.c')
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 117 |
1 files changed, 85 insertions, 32 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 09a790c2f3a1..2f821c58007c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -4,40 +4,43 @@ * Author:Mark Yao <mark.yao@rock-chips.com> */ +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + #include <drm/drm.h> -#include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_uapi.h> #include <drm/drm_crtc.h> #include <drm/drm_flip_work.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_probe_helper.h> +#include <drm/drm_self_refresh_helper.h> +#include <drm/drm_vblank.h> + #ifdef CONFIG_DRM_ANALOGIX_DP #include <drm/bridge/analogix_dp.h> #endif -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/iopoll.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/pm_runtime.h> -#include <linux/component.h> -#include <linux/overflow.h> - -#include <linux/reset.h> -#include <linux/delay.h> - #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" #include "rockchip_drm_fb.h" -#include "rockchip_drm_psr.h" #include "rockchip_drm_vop.h" #include "rockchip_rgb.h" +#define VOP_SELF_REFRESH_ENTRY_DELAY_MS 100 + #define VOP_WIN_SET(vop, win, name, v) \ vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) #define VOP_SCL_SET(vop, win, name, v) \ @@ -79,7 +82,7 @@ vop_get_intr_type(vop, &vop->data->intr->name, type) #define VOP_WIN_GET(vop, win, name) \ - vop_read_reg(vop, win->offset, win->phy->name) + vop_read_reg(vop, win->base, &win->phy->name) #define VOP_WIN_HAS_REG(win, name) \ (!!(win->phy->name.mask)) @@ -124,6 +127,7 @@ struct vop { bool is_enabled; struct completion dsp_hold_completion; + unsigned int win_enabled; /* protected by dev->event_lock */ struct drm_pending_vblank_event *event; @@ -528,8 +532,10 @@ static void vop_core_clks_disable(struct vop *vop) clk_disable(vop->hclk); } -static void vop_win_disable(struct vop *vop, const struct vop_win_data *win) +static void vop_win_disable(struct vop *vop, const struct vop_win *vop_win) { + const struct vop_win_data *win = vop_win->data; + if (win->phy->scl && win->phy->scl->ext) { VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE); VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE); @@ -538,9 +544,10 @@ static void vop_win_disable(struct vop *vop, const struct vop_win_data *win) } VOP_WIN_SET(vop, win, enable, 0); + vop->win_enabled &= ~BIT(VOP_WIN_TO_INDEX(vop_win)); } -static int vop_enable(struct drm_crtc *crtc) +static int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct vop *vop = to_vop(crtc); int ret, i; @@ -580,12 +587,17 @@ static int vop_enable(struct drm_crtc *crtc) * We need to make sure that all windows are disabled before we * enable the crtc. Otherwise we might try to scan from a destroyed * buffer later. + * + * In the case of enable-after-PSR, we don't need to worry about this + * case since the buffer is guaranteed to be valid and disabling the + * window will result in screen glitches on PSR exit. */ - for (i = 0; i < vop->data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - const struct vop_win_data *win = vop_win->data; + if (!old_state || !old_state->self_refresh_active) { + for (i = 0; i < vop->data->win_size; i++) { + struct vop_win *vop_win = &vop->win[i]; - vop_win_disable(vop, win); + vop_win_disable(vop, vop_win); + } } spin_unlock(&vop->reg_lock); @@ -615,6 +627,25 @@ err_put_pm_runtime: return ret; } +static void rockchip_drm_set_win_enabled(struct drm_crtc *crtc, bool enabled) +{ + struct vop *vop = to_vop(crtc); + int i; + + spin_lock(&vop->reg_lock); + + for (i = 0; i < vop->data->win_size; i++) { + struct vop_win *vop_win = &vop->win[i]; + const struct vop_win_data *win = vop_win->data; + + VOP_WIN_SET(vop, win, enable, + enabled && (vop->win_enabled & BIT(i))); + } + vop_cfg_done(vop); + + spin_unlock(&vop->reg_lock); +} + static void vop_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -622,9 +653,16 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, WARN_ON(vop->event); + if (crtc->state->self_refresh_active) + rockchip_drm_set_win_enabled(crtc, false); + mutex_lock(&vop->vop_lock); + drm_crtc_vblank_off(crtc); + if (crtc->state->self_refresh_active) + goto out; + /* * Vop standby will take effect at end of current frame, * if dsp hold valid irq happen, it means standby complete. @@ -655,6 +693,8 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, clk_disable(vop->dclk); vop_core_clks_disable(vop); pm_runtime_put(vop->dev); + +out: mutex_unlock(&vop->vop_lock); if (crtc->state->event && !crtc->state->active) { @@ -726,7 +766,6 @@ static void vop_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { struct vop_win *vop_win = to_vop_win(plane); - const struct vop_win_data *win = vop_win->data; struct vop *vop = to_vop(old_state->crtc); if (!old_state->crtc) @@ -734,7 +773,7 @@ static void vop_plane_atomic_disable(struct drm_plane *plane, spin_lock(&vop->reg_lock); - vop_win_disable(vop, win); + vop_win_disable(vop, vop_win); spin_unlock(&vop->reg_lock); } @@ -873,6 +912,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, } VOP_WIN_SET(vop, win, enable, 1); + vop->win_enabled |= BIT(win_index); spin_unlock(&vop->reg_lock); } @@ -924,12 +964,10 @@ static void vop_plane_atomic_async_update(struct drm_plane *plane, swap(plane->state->fb, new_state->fb); if (vop->is_enabled) { - rockchip_drm_psr_inhibit_get_state(new_state->state); vop_plane_atomic_update(plane, plane->state); spin_lock(&vop->reg_lock); vop_cfg_done(vop); spin_unlock(&vop->reg_lock); - rockchip_drm_psr_inhibit_put_state(new_state->state); /* * A scanout can still be occurring, so we can't drop the @@ -1033,11 +1071,17 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, int dither_bpc = s->output_bpc ? s->output_bpc : 10; int ret; + if (old_state && old_state->self_refresh_active) { + drm_crtc_vblank_on(crtc); + rockchip_drm_set_win_enabled(crtc, true); + return; + } + mutex_lock(&vop->vop_lock); WARN_ON(vop->event); - ret = vop_enable(crtc); + ret = vop_enable(crtc, old_state); if (ret) { mutex_unlock(&vop->vop_lock); DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret); @@ -1519,6 +1563,13 @@ static int vop_create_crtc(struct vop *vop) init_completion(&vop->line_flag_completion); crtc->port = port; + ret = drm_self_refresh_helper_init(crtc, + VOP_SELF_REFRESH_ENTRY_DELAY_MS); + if (ret) + DRM_DEV_DEBUG_KMS(vop->dev, + "Failed to init %s with SR helpers %d, ignoring\n", + crtc->name, ret); + return 0; err_cleanup_crtc: @@ -1536,6 +1587,8 @@ static void vop_destroy_crtc(struct vop *vop) struct drm_device *drm_dev = vop->drm_dev; struct drm_plane *plane, *tmp; + drm_self_refresh_helper_cleanup(crtc); + of_node_put(crtc->port); /* @@ -1560,7 +1613,6 @@ static void vop_destroy_crtc(struct vop *vop) static int vop_initial(struct vop *vop) { - const struct vop_data *vop_data = vop->data; struct reset_control *ahb_rst; int i, ret; @@ -1627,12 +1679,13 @@ static int vop_initial(struct vop *vop) VOP_REG_SET(vop, misc, global_regdone_en, 1); VOP_REG_SET(vop, common, dsp_blank, 0); - for (i = 0; i < vop_data->win_size; i++) { - const struct vop_win_data *win = &vop_data->win[i]; + for (i = 0; i < vop->data->win_size; i++) { + struct vop_win *vop_win = &vop->win[i]; + const struct vop_win_data *win = vop_win->data; int channel = i * 2 + 1; VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel); - vop_win_disable(vop, win); + vop_win_disable(vop, vop_win); VOP_WIN_SET(vop, win, gate, 1); } |