summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 0c0dc808453f..82dafcdd9056 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -17,6 +17,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
#include <drm/drm_plane_helper.h>
#include <linux/kernel.h>
@@ -89,6 +90,10 @@
#define to_vop_win(x) container_of(x, struct vop_win, base)
#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
+enum vop_pending {
+ VOP_PENDING_FB_UNREF,
+};
+
struct vop_plane_state {
struct drm_plane_state base;
int format;
@@ -122,6 +127,9 @@ struct vop {
/* protected by dev->event_lock */
struct drm_pending_vblank_event *event;
+ struct drm_flip_work fb_unref_work;
+ unsigned long pending;
+
struct completion line_flag_completion;
const struct vop_data *data;
@@ -1089,7 +1097,11 @@ static void vop_wait_for_irq_handler(struct vop *vop)
static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
+ struct drm_atomic_state *old_state = old_crtc_state->state;
+ struct drm_plane_state *old_plane_state;
struct vop *vop = to_vop(crtc);
+ struct drm_plane *plane;
+ int i;
if (WARN_ON(!vop->is_enabled))
return;
@@ -1106,6 +1118,19 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
* signalling flip completion we need to wait for it to finish.
*/
vop_wait_for_irq_handler(vop);
+
+ for_each_plane_in_state(old_state, plane, old_plane_state, i) {
+ if (!old_plane_state->fb)
+ continue;
+
+ if (old_plane_state->fb == plane->state->fb)
+ continue;
+
+ drm_framebuffer_reference(old_plane_state->fb);
+ drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb);
+ set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+ }
}
static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
@@ -1181,6 +1206,15 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
.atomic_destroy_state = vop_crtc_destroy_state,
};
+static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
+{
+ struct vop *vop = container_of(work, struct vop, fb_unref_work);
+ struct drm_framebuffer *fb = val;
+
+ drm_crtc_vblank_put(&vop->crtc);
+ drm_framebuffer_unreference(fb);
+}
+
static bool vop_win_pending_is_complete(struct vop_win *vop_win)
{
dma_addr_t yrgb_mst;
@@ -1219,6 +1253,9 @@ static void vop_handle_vblank(struct vop *vop)
if (!completion_done(&vop->wait_update_complete))
complete(&vop->wait_update_complete);
+
+ if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending))
+ drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);
}
static irqreturn_t vop_isr(int irq, void *data)
@@ -1357,6 +1394,9 @@ static int vop_create_crtc(struct vop *vop)
goto err_cleanup_crtc;
}
+ drm_flip_work_init(&vop->fb_unref_work, "fb_unref",
+ vop_fb_unref_worker);
+
init_completion(&vop->dsp_hold_completion);
init_completion(&vop->wait_update_complete);
init_completion(&vop->line_flag_completion);
@@ -1400,6 +1440,7 @@ static void vop_destroy_crtc(struct vop *vop)
* references the CRTC.
*/
drm_crtc_cleanup(crtc);
+ drm_flip_work_cleanup(&vop->fb_unref_work);
}
static int vop_initial(struct vop *vop)
OpenPOWER on IntegriCloud