|
@@ -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)
|