Merge branch 'vsp1/suspend-resume-race-v4-4.10' into vsp1/next
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index b23fa87..b23ba34 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -50,6 +50,7 @@ struct vsp1_uds;
#define VSP1_HAS_CLU (1 << 4)
#define VSP1_HAS_WPF_VFLIP (1 << 5)
#define VSP1_HAS_WPF_HFLIP (1 << 6)
+#define VSP1_HAS_WPF_WRITEBACK (1 << 9)
struct vsp1_device_info {
u32 version;
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index bf735e8..f3966ae 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -25,6 +25,7 @@
#include "vsp1_lif.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
/* -----------------------------------------------------------------------------
@@ -478,6 +479,13 @@ void vsp1_du_atomic_flush(struct device *dev)
__func__, rpf->entity.index);
}
+ /*
+ * If we have a writeback node attached, we use this opportunity to
+ * update the video buffers.
+ */
+ if (pipe->output->video && pipe->output->video->frame_end)
+ pipe->output->video->frame_end(pipe);
+
/* Configure all entities in the pipeline. */
list_for_each_entry(entity, &pipe->entities, list_pipe) {
/* Disconnect unused RPFs from the pipeline. */
@@ -566,6 +574,16 @@ int vsp1_drm_create_links(struct vsp1_device *vsp1)
if (ret < 0)
return ret;
+ if (vsp1->wpf[0]->has_writeback) {
+ /* Connect the video device to the WPF for Writeback support */
+ ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &vsp1->wpf[0]->video->video.entity,
+ 0, flags);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -596,6 +614,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
pipe->bru = &vsp1->bru->entity;
pipe->lif = &vsp1->lif->entity;
pipe->output = vsp1->wpf[0];
+ pipe->output->pipe = pipe;
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index d596cde..0c908cc 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -362,7 +362,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
vsp1->wpf[i] = wpf;
list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
- if (vsp1->info->uapi) {
+ if (vsp1->info->uapi || wpf->has_writeback) {
struct vsp1_video *video = vsp1_video_create(vsp1, wpf);
if (IS_ERR(video)) {
@@ -659,7 +659,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
.model = "VSP2-D",
.gen = 3,
- .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP,
+ .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP
+ | VSP1_HAS_WPF_WRITEBACK,
.rpf_count = 5,
.wpf_count = 2,
.num_bru_inputs = 5,
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 1c98aff..748f6ce 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -53,6 +53,7 @@ struct vsp1_rwpf {
u32 mult_alpha;
u32 outfmt;
+ bool has_writeback;
struct {
spinlock_t lock;
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 84d3538..8041133 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -295,11 +295,6 @@ static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
* This function completes the current buffer by filling its sequence number,
* time stamp and payload size, and hands it back to the videobuf core.
*
- * When operating in DU output mode (deep pipeline to the DU through the LIF),
- * the VSP1 needs to constantly supply frames to the display. In that case, if
- * no other buffer is queued, reuse the one that has just been processed instead
- * of handing it back to the videobuf core.
- *
* Return the next queued buffer or NULL if the queue is empty.
*/
static struct vsp1_vb2_buffer *
@@ -321,12 +316,6 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
done = list_first_entry(&video->irqqueue,
struct vsp1_vb2_buffer, queue);
- /* In DU output mode reuse the buffer if the list is singular. */
- if (pipe->lif && list_is_singular(&video->irqqueue)) {
- spin_unlock_irqrestore(&video->irqlock, flags);
- return done;
- }
-
list_del(&done->queue);
if (!list_empty(&video->irqqueue))
@@ -891,6 +880,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
list_for_each_entry(buffer, &video->irqqueue, queue)
vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&video->irqqueue);
+ INIT_LIST_HEAD(&video->wbqueue);
spin_unlock_irqrestore(&video->irqlock, flags);
}
@@ -904,6 +894,147 @@ static const struct vb2_ops vsp1_video_queue_qops = {
.stop_streaming = vsp1_video_stop_streaming,
};
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations for writeback nodes
+ */
+
+static void vsp1_video_wb_process_buffer(struct vsp1_video *video)
+{
+ struct vsp1_vb2_buffer *buf;
+ unsigned long flags;
+
+ /*
+ * Writeback uses a running stream, unlike the M2M interface which
+ * controls a pipeline process manually though the use of
+ * vsp1_pipeline_run().
+ *
+ * Instead writeback will commence at the next frame interval, and can
+ * be marked complete at the interval following that. To handle this we
+ * store the configured buffer as pending until the next callback.
+ *
+ * | | | | |
+ * A |<-->|
+ * B |<-->|
+ * C |<-->| : Only at interrupt C can A be marked done
+ */
+
+ spin_lock_irqsave(&video->irqlock, flags);
+
+ /* Move the pending image to the active hw queue */
+ if (video->pending) {
+ list_add_tail(&video->pending->queue, &video->irqqueue);
+ video->pending = NULL;
+ }
+
+ buf = list_first_entry_or_null(&video->wbqueue, struct vsp1_vb2_buffer,
+ queue);
+
+ if (buf) {
+ video->rwpf->mem = buf->mem;
+
+ /*
+ * Store this buffer as pending. It will commence at the next
+ * frame start interrupt
+ */
+ video->pending = buf;
+ list_del(&buf->queue);
+ } else {
+ /* Disable writeback with no buffer */
+ video->rwpf->mem = (struct vsp1_rwpf_memory) { 0 };
+ }
+
+ spin_unlock_irqrestore(&video->irqlock, flags);
+}
+
+static void vsp1_video_wb_frame_end(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_video *video = pipe->output->video;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ /* Complete any buffer on the IRQ queue */
+ vsp1_video_complete_buffer(video);
+
+ /* Queue up any buffer from our wb queue, and place on the IRQ queue */
+ vsp1_video_wb_process_buffer(video);
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+static void vsp1_video_wb_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
+ unsigned long flags;
+
+ spin_lock_irqsave(&video->irqlock, flags);
+ list_add_tail(&buf->queue, &video->wbqueue);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+}
+
+static int vsp1_video_wb_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ unsigned long flags;
+
+ /* Enable the completion interrupts */
+ spin_lock_irqsave(&video->irqlock, flags);
+ video->frame_end = vsp1_video_wb_frame_end;
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ return 0;
+}
+
+static void vsp1_video_wb_stop_streaming(struct vb2_queue *vq)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ struct vsp1_rwpf *rwpf = video->rwpf;
+ struct vsp1_pipeline *pipe = rwpf->pipe;
+ struct vsp1_vb2_buffer *buffer;
+ unsigned long flags;
+
+ /*
+ * Disable the completion interrupts, and clear the WPF memory to
+ * prevent writing out frames
+ */
+ spin_lock_irqsave(&video->irqlock, flags);
+ video->frame_end = NULL;
+ rwpf->mem = (struct vsp1_rwpf_memory) { 0 };
+
+ /* Return all queued buffers to userspace */
+ list_for_each_entry(buffer, &video->wbqueue, queue)
+ vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
+ list_for_each_entry(buffer, &video->irqqueue, queue)
+ vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
+ if (video->pending) {
+ vb2_buffer_done(&video->pending->buf.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ video->pending = NULL;
+ }
+
+ INIT_LIST_HEAD(&video->wbqueue);
+ INIT_LIST_HEAD(&video->irqqueue);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ /* Return the reference obtained by vsp1_video_streamon() */
+ vsp1_video_pipeline_put(pipe);
+}
+
+static const struct vb2_ops vsp1_video_wb_queue_qops = {
+ .queue_setup = vsp1_video_queue_setup,
+ .buf_prepare = vsp1_video_buffer_prepare,
+ .buf_queue = vsp1_video_wb_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = vsp1_video_wb_start_streaming,
+ .stop_streaming = vsp1_video_wb_stop_streaming,
+};
+
+
/* -----------------------------------------------------------------------------
* V4L2 ioctls
*/
@@ -1140,6 +1271,8 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
video->vsp1 = vsp1;
video->rwpf = rwpf;
+ video->is_writeback = rwpf->has_writeback;
+
if (rwpf->entity.type == VSP1_ENTITY_RPF) {
direction = "input";
video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
@@ -1155,6 +1288,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
mutex_init(&video->lock);
spin_lock_init(&video->irqlock);
INIT_LIST_HEAD(&video->irqqueue);
+ INIT_LIST_HEAD(&video->wbqueue);
/* Initialize the media entity... */
ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
@@ -1178,12 +1312,15 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
video_set_drvdata(&video->video, video);
+ if (video->is_writeback)
+ video->queue.ops = &vsp1_video_wb_queue_qops;
+ else
+ video->queue.ops = &vsp1_video_queue_qops;
video->queue.type = video->type;
video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
video->queue.lock = &video->lock;
video->queue.drv_priv = video;
video->queue.buf_struct_size = sizeof(struct vsp1_vb2_buffer);
- video->queue.ops = &vsp1_video_queue_qops;
video->queue.mem_ops = &vb2_dma_contig_memops;
video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
video->queue.dev = video->vsp1->dev;
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
index 50ea7f0..b63e14b 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -48,6 +48,11 @@ struct vsp1_video {
struct vb2_queue queue;
spinlock_t irqlock;
struct list_head irqqueue;
+
+ bool is_writeback;
+ struct list_head wbqueue;
+ struct vsp1_vb2_buffer *pending;
+ void (*frame_end)(struct vsp1_pipeline *pipe);
};
static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 7c48f81..1a4c6a2 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -186,6 +186,8 @@ static void wpf_configure(struct vsp1_entity *entity,
u32 outfmt = 0;
u32 srcrpf = 0;
+ bool writeback = pipe->lif && wpf->mem.addr[0];
+
if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
const unsigned int mask = BIT(WPF_CTRL_VFLIP)
| BIT(WPF_CTRL_HFLIP);
@@ -236,7 +238,14 @@ static void wpf_configure(struct vsp1_entity *entity,
(0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
(height << VI6_WPF_SZCLIP_SIZE_SHIFT));
- if (pipe->lif)
+ vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, writeback ?
+ VI6_WPF_WRBCK_CTRL_WBMD : 0);
+
+ /*
+ * Display pipelines with no writeback memory do not configure
+ * the write out address
+ */
+ if (pipe->lif && !writeback)
return;
/*
@@ -288,7 +297,7 @@ static void wpf_configure(struct vsp1_entity *entity,
}
/* Format */
- if (!pipe->lif) {
+ if (!pipe->lif || writeback) {
const struct v4l2_pix_format_mplane *format = &wpf->format;
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
@@ -325,8 +334,6 @@ static void wpf_configure(struct vsp1_entity *entity,
vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
- vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0);
-
/* Sources. If the pipeline has a single input and BRU is not used,
* configure it as the master layer. Otherwise configure all
* inputs as sub-layers and select the virtual RPF as the master
@@ -385,6 +392,10 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
wpf->entity.type = VSP1_ENTITY_WPF;
wpf->entity.index = index;
+ /* WPFs with writeback support can output to the LIF and memory */
+ wpf->has_writeback = (vsp1->info->features & VSP1_HAS_WPF_WRITEBACK)
+ && index == 0;
+
sprintf(name, "wpf.%u", index);
ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);