All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/19] R-Car DU display writeback support
@ 2019-02-21 10:31 Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously" Laurent Pinchart
                   ` (19 more replies)
  0 siblings, 20 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

Hello everybody,

This patch series implements display writeback support for the R-Car
Gen3 platforms in the VSP1 and DU drivers.

Patches 01/19 to 11/19 prepare the VSP1 driver for writeback support
with all the necessary plumbing, including extensions of the API between
the VSP1 and DU drivers.

Compared to v4 the major change is the move from V4L2 to DRM writeback
connectors for the userspace API. This has caused a few issues with
writeback support to be uncovered, and they are addressed by patches
12/19 to 14/19. Patch 15/19 is an unrelated drive-by fix.

Patches 16/19 to 18/19 then perform refactoring of the DU driver, to
finally add writeback support in patch 19/19.

The writeback pixel format is restricted to RGB, due to the VSP1
outputting RGB to the display and lacking a separate colour space
conversion unit for writeback. The resolution can be freely picked by
will result in cropping or composing, not scaling.

Writeback requests are queued to the hardware on page flip (atomic
flush), and complete at the next vblank. This means that a queued
writeback buffer will not be processed until the next page flip, but
once it starts being written to by the VSP, it will complete at the next
vblank regardless of whether another page flip occurs at that time.

The code is based on a merge of the media master branch, the drm-next
branch and the R-Car DT next branch. For convenience patches can be
found at

        git://linuxtv.org/pinchartl/media.git v4l2/vsp1/writeback

Kieran Bingham (1):
  Revert "[media] v4l: vsp1: Supply frames to the DU continuously"

Laurent Pinchart (18):
  media: vsp1: wpf: Fix partition configuration for display pipelines
  media: vsp1: Replace leftover occurrence of fragment with body
  media: vsp1: Fix addresses of display-related registers for VSP-DL
  media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse
  media: vsp1: Replace the display list internal flag with a flags field
  media: vsp1: dl: Support one-shot entries in the display list
  media: vsp1: wpf: Add writeback support
  media: vsp1: drm: Split RPF format setting to separate function
  media: vsp1: drm: Extend frame completion API to the DU driver
  media: vsp1: drm: Implement writeback support
  drm: writeback: Cleanup job ownership handling when queuing job
  drm: writeback: Fix leak of writeback job
  drm: writeback: Add job prepare and cleanup operations
  drm/msm: Remove prototypes for non-existing functions
  drm: rcar-du: Fix rcar_du_crtc structure documentation
  drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure
  drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate
    functions
  drm: rcar-du: Add writeback support for R-Car Gen3

 drivers/gpu/drm/arm/malidp_mw.c             |   3 +-
 drivers/gpu/drm/drm_atomic_helper.c         |  11 ++
 drivers/gpu/drm/drm_atomic_state_helper.c   |   4 +
 drivers/gpu/drm/drm_atomic_uapi.c           |  31 +--
 drivers/gpu/drm/drm_writeback.c             |  71 ++++++-
 drivers/gpu/drm/msm/msm_drv.h               |   2 -
 drivers/gpu/drm/rcar-du/Kconfig             |   4 +
 drivers/gpu/drm/rcar-du/Makefile            |   3 +-
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h      |   9 +-
 drivers/gpu/drm/rcar-du/rcar_du_kms.c       |  37 ++++
 drivers/gpu/drm/rcar-du/rcar_du_kms.h       |   1 +
 drivers/gpu/drm/rcar-du/rcar_du_vsp.c       | 121 ++++++------
 drivers/gpu/drm/rcar-du/rcar_du_vsp.h       |  17 ++
 drivers/gpu/drm/rcar-du/rcar_du_writeback.c | 203 ++++++++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_writeback.h |  39 ++++
 drivers/gpu/drm/vc4/vc4_txp.c               |   2 +-
 drivers/media/platform/vsp1/vsp1_dl.c       | 127 ++++++++++--
 drivers/media/platform/vsp1/vsp1_dl.h       |   8 +-
 drivers/media/platform/vsp1/vsp1_drm.c      |  92 ++++++---
 drivers/media/platform/vsp1/vsp1_drm.h      |   2 +-
 drivers/media/platform/vsp1/vsp1_drv.c      |  15 ++
 drivers/media/platform/vsp1/vsp1_pipe.c     |   5 +
 drivers/media/platform/vsp1/vsp1_pipe.h     |   1 +
 drivers/media/platform/vsp1/vsp1_regs.h     |   6 +-
 drivers/media/platform/vsp1/vsp1_rwpf.h     |   2 +
 drivers/media/platform/vsp1/vsp1_video.c    |  49 +++--
 drivers/media/platform/vsp1/vsp1_wpf.c      |  68 +++++--
 include/drm/drm_modeset_helper_vtables.h    |   7 +
 include/drm/drm_writeback.h                 |  30 ++-
 include/media/vsp1.h                        |  19 +-
 30 files changed, 790 insertions(+), 199 deletions(-)
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.c
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.h

-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously"
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
@ 2019-02-21 10:31 ` Laurent Pinchart
  2019-02-21 13:16   ` Kieran Bingham
  2019-02-21 10:31 ` [PATCH v5 02/19] media: vsp1: wpf: Fix partition configuration for display pipelines Laurent Pinchart
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

This reverts commit 3299ba5c0b21 ("[media] v4l: vsp1: Supply frames to
the DU continuously")

The DU output mode does not rely on frames being supplied on the WPF as
its pipeline is supplied from DRM. For the upcoming WPF writeback
functionality, we will choose to enable writeback mode if there is an
output buffer, or disable it (leaving the existing display pipeline
unharmed) otherwise.

Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_video.c | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 7ceaf3222145..328d686189be 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -307,11 +307,6 @@ static int 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 *
@@ -333,12 +328,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))
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 02/19] media: vsp1: wpf: Fix partition configuration for display pipelines
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously" Laurent Pinchart
@ 2019-02-21 10:31 ` Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 03/19] media: vsp1: Replace leftover occurrence of fragment with body Laurent Pinchart
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

When configuring partitions for memory-to-memory pipelines, the WPF
accesses data of the current partition through pipe->partition.
Writeback support will require full configuration of the WPF while not
providing a valid pipe->partition. Rework the configuration code to fall
back to the full image width in that case, as is already done for the
part of the configuration currently relevant for display pipelines.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_wpf.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 32bb207b2007..a07c5944b598 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -362,6 +362,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
 	const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
 	unsigned int width;
 	unsigned int height;
+	unsigned int left;
 	unsigned int offset;
 	unsigned int flip;
 	unsigned int i;
@@ -371,13 +372,16 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
 						 RWPF_PAD_SINK);
 	width = sink_format->width;
 	height = sink_format->height;
+	left = 0;
 
 	/*
 	 * Cropping. The partition algorithm can split the image into
 	 * multiple slices.
 	 */
-	if (pipe->partitions > 1)
+	if (pipe->partitions > 1) {
 		width = pipe->partition->wpf.width;
+		left = pipe->partition->wpf.left;
+	}
 
 	vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
 		       (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
@@ -408,13 +412,11 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
 	flip = wpf->flip.active;
 
 	if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
-		offset = format->width - pipe->partition->wpf.left
-			- pipe->partition->wpf.width;
+		offset = format->width - left - width;
 	else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
-		offset = format->height - pipe->partition->wpf.left
-			- pipe->partition->wpf.width;
+		offset = format->height - left - width;
 	else
-		offset = pipe->partition->wpf.left;
+		offset = left;
 
 	for (i = 0; i < format->num_planes; ++i) {
 		unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
@@ -436,7 +438,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
 		 * image height.
 		 */
 		if (wpf->flip.rotate)
-			height = pipe->partition->wpf.width;
+			height = width;
 		else
 			height = format->height;
 
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 03/19] media: vsp1: Replace leftover occurrence of fragment with body
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously" Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 02/19] media: vsp1: wpf: Fix partition configuration for display pipelines Laurent Pinchart
@ 2019-02-21 10:31 ` Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 04/19] media: vsp1: Fix addresses of display-related registers for VSP-DL Laurent Pinchart
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

Display list fragments have been renamed to bodies. Replace one last
occurrence of the word fragment in the documentation.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_dl.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 26289adaf658..64af449791b0 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -699,8 +699,8 @@ struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl)
  * which bodies are added.
  *
  * Adding a body to a display list passes ownership of the body to the list. The
- * caller retains its reference to the fragment when adding it to the display
- * list, but is not allowed to add new entries to the body.
+ * caller retains its reference to the body when adding it to the display list,
+ * but is not allowed to add new entries to the body.
  *
  * The reference must be explicitly released by a call to vsp1_dl_body_put()
  * when the body isn't needed anymore.
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 04/19] media: vsp1: Fix addresses of display-related registers for VSP-DL
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (2 preceding siblings ...)
  2019-02-21 10:31 ` [PATCH v5 03/19] media: vsp1: Replace leftover occurrence of fragment with body Laurent Pinchart
@ 2019-02-21 10:31 ` Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 05/19] media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse Laurent Pinchart
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The VSP-DL instances have two LIFs, and thus two copies of the
VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_STA and VI6_WPF_WRBCK_CTRL registers. Fix
the corresponding macros accordingly.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_drm.c  | 4 ++--
 drivers/media/platform/vsp1/vsp1_regs.h | 6 +++---
 drivers/media/platform/vsp1/vsp1_wpf.c  | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 8d86f618ec77..048190fd3a2d 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -700,8 +700,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
 	drm_pipe->du_private = cfg->callback_data;
 
 	/* Disable the display interrupts. */
-	vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
-	vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
+	vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0);
+	vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0);
 
 	/* Configure all entities in the pipeline. */
 	vsp1_du_pipeline_configure(pipe);
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index f6e4157095cc..1bb1d39c60d9 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -39,12 +39,12 @@
 #define VI6_WFP_IRQ_STA_DFE		(1 << 1)
 #define VI6_WFP_IRQ_STA_FRE		(1 << 0)
 
-#define VI6_DISP_IRQ_ENB		0x0078
+#define VI6_DISP_IRQ_ENB(n)		(0x0078 + (n) * 60)
 #define VI6_DISP_IRQ_ENB_DSTE		(1 << 8)
 #define VI6_DISP_IRQ_ENB_MAEE		(1 << 5)
 #define VI6_DISP_IRQ_ENB_LNEE(n)	(1 << (n))
 
-#define VI6_DISP_IRQ_STA		0x007c
+#define VI6_DISP_IRQ_STA(n)		(0x007c + (n) * 60)
 #define VI6_DISP_IRQ_STA_DST		(1 << 8)
 #define VI6_DISP_IRQ_STA_MAE		(1 << 5)
 #define VI6_DISP_IRQ_STA_LNE(n)		(1 << (n))
@@ -307,7 +307,7 @@
 #define VI6_WPF_DSTM_ADDR_C0		0x1028
 #define VI6_WPF_DSTM_ADDR_C1		0x102c
 
-#define VI6_WPF_WRBCK_CTRL		0x1034
+#define VI6_WPF_WRBCK_CTRL(n)		(0x1034 + (n) * 0x100)
 #define VI6_WPF_WRBCK_CTRL_WBMD		(1 << 0)
 
 /* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index a07c5944b598..18c49e3a7875 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -291,7 +291,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
 	vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index),
 			   VI6_DPR_WPF_FPORCH_FP_WPFN);
 
-	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL, 0);
+	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(wpf->entity.index), 0);
 
 	/*
 	 * Sources. If the pipeline has a single input and BRx is not used,
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 05/19] media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (3 preceding siblings ...)
  2019-02-21 10:31 ` [PATCH v5 04/19] media: vsp1: Fix addresses of display-related registers for VSP-DL Laurent Pinchart
@ 2019-02-21 10:31 ` Laurent Pinchart
  2019-02-21 10:31 ` [PATCH v5 06/19] media: vsp1: Replace the display list internal flag with a flags field Laurent Pinchart
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The vsp1_video_complete_buffer() function completes the current buffer
and returns a pointer to the next buffer. Split the code that completes
the buffer to a separate function for later reuse, and rename
vsp1_video_complete_buffer() to vsp1_video_complete_next_buffer().

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_video.c | 36 ++++++++++++++----------
 1 file changed, 21 insertions(+), 15 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 328d686189be..2f3d6153fabd 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -300,8 +300,23 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
  * Pipeline Management
  */
 
+static void vsp1_video_complete_buffer(struct vsp1_video *video,
+				       struct vsp1_vb2_buffer *buffer)
+{
+	struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
+	unsigned int i;
+
+	buffer->buf.sequence = pipe->sequence;
+	buffer->buf.vb2_buf.timestamp = ktime_get_ns();
+	for (i = 0; i < buffer->buf.vb2_buf.num_planes; ++i)
+		vb2_set_plane_payload(&buffer->buf.vb2_buf, i,
+				      vb2_plane_size(&buffer->buf.vb2_buf, i));
+	vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
 /*
- * vsp1_video_complete_buffer - Complete the current buffer
+ * vsp1_video_complete_next_buffer - Complete the current buffer and return the
+ *	next buffer
  * @video: the video node
  *
  * This function completes the current buffer by filling its sequence number,
@@ -310,13 +325,11 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
  * Return the next queued buffer or NULL if the queue is empty.
  */
 static struct vsp1_vb2_buffer *
-vsp1_video_complete_buffer(struct vsp1_video *video)
+vsp1_video_complete_next_buffer(struct vsp1_video *video)
 {
-	struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
-	struct vsp1_vb2_buffer *next = NULL;
+	struct vsp1_vb2_buffer *next;
 	struct vsp1_vb2_buffer *done;
 	unsigned long flags;
-	unsigned int i;
 
 	spin_lock_irqsave(&video->irqlock, flags);
 
@@ -327,21 +340,14 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
 
 	done = list_first_entry(&video->irqqueue,
 				struct vsp1_vb2_buffer, queue);
-
 	list_del(&done->queue);
 
-	if (!list_empty(&video->irqqueue))
-		next = list_first_entry(&video->irqqueue,
+	next = list_first_entry_or_null(&video->irqqueue,
 					struct vsp1_vb2_buffer, queue);
 
 	spin_unlock_irqrestore(&video->irqlock, flags);
 
-	done->buf.sequence = pipe->sequence;
-	done->buf.vb2_buf.timestamp = ktime_get_ns();
-	for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
-		vb2_set_plane_payload(&done->buf.vb2_buf, i,
-				      vb2_plane_size(&done->buf.vb2_buf, i));
-	vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE);
+	vsp1_video_complete_buffer(video, done);
 
 	return next;
 }
@@ -352,7 +358,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
 	struct vsp1_video *video = rwpf->video;
 	struct vsp1_vb2_buffer *buf;
 
-	buf = vsp1_video_complete_buffer(video);
+	buf = vsp1_video_complete_next_buffer(video);
 	if (buf == NULL)
 		return;
 
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 06/19] media: vsp1: Replace the display list internal flag with a flags field
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (4 preceding siblings ...)
  2019-02-21 10:31 ` [PATCH v5 05/19] media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse Laurent Pinchart
@ 2019-02-21 10:31 ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list Laurent Pinchart
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

To prepare for addition of more flags to the display list, replace the
'internal' flag field by a bitmask 'flags' field.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
Changes since v4:

- Fix check for the completed flag in vsp1_du_pipeline_frame_end()
---
 drivers/media/platform/vsp1/vsp1_dl.c    | 31 +++++++++++++-----------
 drivers/media/platform/vsp1/vsp1_dl.h    |  2 +-
 drivers/media/platform/vsp1/vsp1_drm.c   |  8 ++++--
 drivers/media/platform/vsp1/vsp1_video.c |  2 +-
 4 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 64af449791b0..886b3a69d329 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -178,7 +178,7 @@ struct vsp1_dl_cmd_pool {
  * @post_cmd: post command to be issued through extended dl header
  * @has_chain: if true, indicates that there's a partition chain
  * @chain: entry in the display list partition chain
- * @internal: whether the display list is used for internal purpose
+ * @flags: display list flags, a combination of VSP1_DL_FRAME_END_*
  */
 struct vsp1_dl_list {
 	struct list_head list;
@@ -197,7 +197,7 @@ struct vsp1_dl_list {
 	bool has_chain;
 	struct list_head chain;
 
-	bool internal;
+	unsigned int flags;
 };
 
 /**
@@ -861,13 +861,15 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl)
 	 *
 	 * If a display list is already pending we simply drop it as the new
 	 * display list is assumed to contain a more recent configuration. It is
-	 * an error if the already pending list has the internal flag set, as
-	 * there is then a process waiting for that list to complete. This
-	 * shouldn't happen as the waiting process should perform proper
-	 * locking, but warn just in case.
+	 * an error if the already pending list has the
+	 * VSP1_DL_FRAME_END_INTERNAL flag set, as there is then a process
+	 * waiting for that list to complete. This shouldn't happen as the
+	 * waiting process should perform proper locking, but warn just in
+	 * case.
 	 */
 	if (vsp1_dl_list_hw_update_pending(dlm)) {
-		WARN_ON(dlm->pending && dlm->pending->internal);
+		WARN_ON(dlm->pending &&
+			(dlm->pending->flags & VSP1_DL_FRAME_END_INTERNAL));
 		__vsp1_dl_list_put(dlm->pending);
 		dlm->pending = dl;
 		return;
@@ -897,7 +899,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl)
 	dlm->active = dl;
 }
 
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal)
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
 {
 	struct vsp1_dl_manager *dlm = dl->dlm;
 	struct vsp1_dl_list *dl_next;
@@ -912,7 +914,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal)
 		vsp1_dl_list_fill_header(dl_next, last);
 	}
 
-	dl->internal = internal;
+	dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED;
 
 	spin_lock_irqsave(&dlm->lock, flags);
 
@@ -941,9 +943,10 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal)
  * set in single-shot mode as display list processing is then not continuous and
  * races never occur.
  *
- * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list
- * has completed and had been queued with the internal notification flag.
- * Internal notification is only supported for continuous mode.
+ * The following flags are only supported for continuous mode.
+ *
+ * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just
+ * became active had been queued with the internal notification flag.
  */
 unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
 {
@@ -986,9 +989,9 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
 	 * frame end interrupt. The display list thus becomes active.
 	 */
 	if (dlm->queued) {
-		if (dlm->queued->internal)
+		if (dlm->queued->flags & VSP1_DL_FRAME_END_INTERNAL)
 			flags |= VSP1_DL_FRAME_END_INTERNAL;
-		dlm->queued->internal = false;
+		dlm->queued->flags &= ~VSP1_DL_FRAME_END_INTERNAL;
 
 		__vsp1_dl_list_put(dlm->active);
 		dlm->active = dlm->queued;
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index 125750dc8b5c..e0fdb145e6ed 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -61,7 +61,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
 void vsp1_dl_list_put(struct vsp1_dl_list *dl);
 struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl);
 struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl);
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal);
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags);
 
 struct vsp1_dl_body_pool *
 vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 048190fd3a2d..89773d3a916c 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -34,7 +34,7 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
 				       unsigned int completion)
 {
 	struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
-	bool complete = completion == VSP1_DL_FRAME_END_COMPLETED;
+	bool complete = completion & VSP1_DL_FRAME_END_COMPLETED;
 
 	if (drm_pipe->du_complete) {
 		struct vsp1_entity *uif = drm_pipe->uif;
@@ -537,6 +537,10 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
 	struct vsp1_entity *next;
 	struct vsp1_dl_list *dl;
 	struct vsp1_dl_body *dlb;
+	unsigned int dl_flags = 0;
+
+	if (drm_pipe->force_brx_release)
+		dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
 
 	dl = vsp1_dl_list_get(pipe->output->dlm);
 	dlb = vsp1_dl_list_get_body0(dl);
@@ -559,7 +563,7 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
 		vsp1_entity_configure_partition(entity, pipe, dl, dlb);
 	}
 
-	vsp1_dl_list_commit(dl, drm_pipe->force_brx_release);
+	vsp1_dl_list_commit(dl, dl_flags);
 }
 
 /* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 2f3d6153fabd..e35981e0ae63 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -427,7 +427,7 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
 	}
 
 	/* Complete, and commit the head display list. */
-	vsp1_dl_list_commit(dl, false);
+	vsp1_dl_list_commit(dl, 0);
 	pipe->configured = true;
 
 	vsp1_pipeline_run(pipe);
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (5 preceding siblings ...)
  2019-02-21 10:31 ` [PATCH v5 06/19] media: vsp1: Replace the display list internal flag with a flags field Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 13:16   ` Kieran Bingham
  2019-02-22 14:30   ` Brian Starkey
  2019-02-21 10:32 ` [PATCH v5 08/19] media: vsp1: wpf: Add writeback support Laurent Pinchart
                   ` (12 subsequent siblings)
  19 siblings, 2 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

One-shot entries are used as an alternative to committing a complete new
display list when a couple of registers need to be written for one frame
and then reset to another value for all subsequent frames. This will be
used to implement writeback support that will need to enable writeback
for the duration of a single frame.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
 drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
 2 files changed, 81 insertions(+)

diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 886b3a69d329..7b4d252bfde7 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -115,6 +115,12 @@ struct vsp1_dl_body {
 
 	unsigned int num_entries;
 	unsigned int max_entries;
+
+	unsigned int num_patches;
+	struct {
+		struct vsp1_dl_entry *entry;
+		u32 data;
+	} patches[2];
 };
 
 /**
@@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
 		return;
 
 	dlb->num_entries = 0;
+	dlb->num_patches = 0;
 
 	spin_lock_irqsave(&dlb->pool->lock, flags);
 	list_add_tail(&dlb->free, &dlb->pool->free);
@@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
 	dlb->num_entries++;
 }
 
+/**
+ * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
+ *	single frame
+ * @dlb: The body
+ * @reg: The register address
+ * @value: The register value
+ * @reset_value: The value to reset the register to at the next vblank
+ *
+ * Display lists in continuous mode are re-used by the hardware for successive
+ * frames until a new display list is committed. Changing the VSP configuration
+ * normally requires creating and committing a new display list. This function
+ * offers an alternative race-free way by writing a @value to the @register in
+ * the display list body for a single frame, specifying in @reset_value the
+ * value to reset the register to one vblank after the display list is
+ * committed.
+ *
+ * The maximum number of one-shot entries is limited to 2 per display list body,
+ * and one-shot entries are counted in the total number of entries specified
+ * when the body is allocated by vsp1_dl_body_alloc().
+ */
+void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
+				u32 reset_value)
+{
+	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
+		      "DLB size exceeded (max %u)", dlb->max_entries))
+		return;
+
+	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
+		      "DLB patches size exceeded (max %zu)",
+		      ARRAY_SIZE(dlb->patches)))
+		return;
+
+	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
+	dlb->patches[dlb->num_patches].data = reset_value;
+	dlb->num_patches++;
+
+	dlb->entries[dlb->num_entries].addr = reg;
+	dlb->entries[dlb->num_entries].data = value;
+	dlb->num_entries++;
+}
+
 /* -----------------------------------------------------------------------------
  * Display List Extended Command Management
  */
@@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
 	 * has at least one body, thus we reinitialise the entries list.
 	 */
 	dl->body0->num_entries = 0;
+	dl->body0->num_patches = 0;
 
 	list_add_tail(&dl->list, &dl->dlm->free);
 }
@@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
  * Display List Manager
  */
 
+/**
+ * vsp1_dlm_irq_display_start - Display list handler for the display start
+ *	interrupt
+ * @dlm: the display list manager
+ *
+ * Apply all one-shot patches registered for the active display list.
+ */
+void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
+{
+	struct vsp1_dl_body *dlb;
+	struct vsp1_dl_list *dl;
+	unsigned int i;
+
+	spin_lock(&dlm->lock);
+
+	dl = dlm->active;
+	if (!dl)
+		goto done;
+
+	list_for_each_entry(dlb, &dl->bodies, list) {
+		for (i = 0; i < dlb->num_patches; ++i)
+			dlb->patches[i].entry->data = dlb->patches[i].data;
+		dlb->num_patches = 0;
+	}
+
+done:
+	spin_unlock(&dlm->lock);
+}
+
 /**
  * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
  * @dlm: the display list manager
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index e0fdb145e6ed..f845607abc4c 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
 					unsigned int prealloc);
 void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
 void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
+void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
 unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
 struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
 
@@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
 void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
 
 void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
+void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
+				u32 reset_value);
 int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
 int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
 
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 08/19] media: vsp1: wpf: Add writeback support
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (6 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 09/19] media: vsp1: drm: Split RPF format setting to separate function Laurent Pinchart
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

Add support for the writeback feature of the WPF, to enable capturing
frames at the WPF output for display pipelines. To enable writeback the
vsp1_rwpf structure mem field must be set to the address of the
writeback buffer and the writeback field set to true before the WPF
.configure_stream() and .configure_partition() are called. The WPF will
enable writeback in the display list for a single frame, and writeback
will then be automatically disabled.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_drv.c  | 15 +++++++
 drivers/media/platform/vsp1/vsp1_pipe.c |  5 +++
 drivers/media/platform/vsp1/vsp1_pipe.h |  1 +
 drivers/media/platform/vsp1/vsp1_rwpf.h |  2 +
 drivers/media/platform/vsp1/vsp1_wpf.c  | 52 ++++++++++++++++++++-----
 5 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index c650e45bb0ad..22709f6f2a0c 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -63,6 +63,21 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
 			vsp1_pipeline_frame_end(wpf->entity.pipe);
 			ret = IRQ_HANDLED;
 		}
+
+		/*
+		 * Process the display start interrupt after the frame end
+		 * interrupt to make sure the display list queue is correctly
+		 * updated when processing the display start.
+		 */
+		if (wpf->has_writeback) {
+			status = vsp1_read(vsp1, VI6_DISP_IRQ_STA(i));
+			vsp1_write(vsp1, VI6_DISP_IRQ_STA(i), ~status & mask);
+
+			if (status & VI6_DISP_IRQ_STA_DST) {
+				vsp1_pipeline_display_start(wpf->entity.pipe);
+				ret = IRQ_HANDLED;
+			}
+		}
 	}
 
 	return ret;
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index 54ff539ffea0..016800c45bc1 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -309,6 +309,11 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
 	return pipe->buffers_ready == mask;
 }
 
+void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe)
+{
+	vsp1_dlm_irq_display_start(pipe->output->dlm);
+}
+
 void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
 {
 	unsigned int flags;
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h
index ae646c9ef337..801fca13fc4f 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/vsp1/vsp1_pipe.h
@@ -158,6 +158,7 @@ bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe);
 int vsp1_pipeline_stop(struct vsp1_pipeline *pipe);
 bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
 
+void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe);
 void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
 
 void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 70742ecf766f..910990b27617 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -35,6 +35,7 @@ struct vsp1_rwpf {
 	struct v4l2_ctrl_handler ctrls;
 
 	struct vsp1_video *video;
+	bool has_writeback;
 
 	unsigned int max_width;
 	unsigned int max_height;
@@ -61,6 +62,7 @@ struct vsp1_rwpf {
 	} flip;
 
 	struct vsp1_rwpf_memory mem;
+	bool writeback;
 
 	struct vsp1_dl_manager *dlm;
 };
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 18c49e3a7875..b8bcc7968f9c 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -240,6 +240,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
 	struct vsp1_device *vsp1 = wpf->entity.vsp1;
 	const struct v4l2_mbus_framefmt *source_format;
 	const struct v4l2_mbus_framefmt *sink_format;
+	unsigned int index = wpf->entity.index;
 	unsigned int i;
 	u32 outfmt = 0;
 	u32 srcrpf = 0;
@@ -250,8 +251,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
 	source_format = vsp1_entity_get_pad_format(&wpf->entity,
 						   wpf->entity.config,
 						   RWPF_PAD_SOURCE);
+
 	/* Format */
-	if (!pipe->lif) {
+	if (!pipe->lif || wpf->writeback) {
 		const struct v4l2_pix_format_mplane *format = &wpf->format;
 		const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
 
@@ -276,8 +278,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
 
 		vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap);
 
-		if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) &&
-		    wpf->entity.index == 0)
+		if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && index == 0)
 			vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL,
 				       VI6_WPF_ROT_CTRL_LN16 |
 				       (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
@@ -288,11 +289,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
 
 	wpf->outfmt = outfmt;
 
-	vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+	vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(index),
 			   VI6_DPR_WPF_FPORCH_FP_WPFN);
 
-	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(wpf->entity.index), 0);
-
 	/*
 	 * Sources. If the pipeline has a single input and BRx is not used,
 	 * configure it as the master layer. Otherwise configure all
@@ -318,9 +317,27 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
 	vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf);
 
 	/* Enable interrupts. */
-	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
-	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index),
+	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(index), 0);
+	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index),
 			   VI6_WFP_IRQ_ENB_DFEE);
+
+	/*
+	 * Configure writeback for display pipelines. The wpf writeback flag is
+	 * never set for memory-to-memory pipelines. For display pipelines that
+	 * have a writeback buffer, enable the display start interrupt and the
+	 * writeback for one frame and reset the registers to 0 on the next
+	 * vblank.
+	 */
+	vsp1_dl_body_write(dlb, VI6_DISP_IRQ_STA(index), 0);
+	if (wpf->writeback) {
+		vsp1_dl_body_write_oneshot(dlb, VI6_DISP_IRQ_ENB(index),
+					   VI6_DISP_IRQ_ENB_DSTE, 0);
+		vsp1_dl_body_write_oneshot(dlb, VI6_WPF_WRBCK_CTRL(index),
+					   VI6_WPF_WRBCK_CTRL_WBMD, 0);
+	} else {
+		vsp1_dl_body_write(dlb, VI6_DISP_IRQ_ENB(index), 0);
+		vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 0);
+	}
 }
 
 static void wpf_configure_frame(struct vsp1_entity *entity,
@@ -390,7 +407,11 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
 		       (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
 		       (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
 
-	if (pipe->lif)
+	/*
+	 * For display pipelines without writeback enabled there's no memory
+	 * address to configure, return now.
+	 */
+	if (pipe->lif && !wpf->writeback)
 		return;
 
 	/*
@@ -479,6 +500,12 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
 	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
 	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
 	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
+
+	/*
+	 * Writeback operates in single-shot mode and lasts for a single frame,
+	 * reset the writeback flag to false for the next frame.
+	 */
+	wpf->writeback = false;
 }
 
 static unsigned int wpf_max_width(struct vsp1_entity *entity,
@@ -529,6 +556,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
 		wpf->max_height = WPF_GEN3_MAX_HEIGHT;
 	}
 
+	/*
+	 * On Gen3 WPFs with a LIF output can also write to memory for display
+	 * writeback.
+	 */
+	if (vsp1->info->gen > 2 && index < vsp1->info->lif_count)
+		wpf->has_writeback = true;
+
 	wpf->entity.ops = &wpf_entity_ops;
 	wpf->entity.type = VSP1_ENTITY_WPF;
 	wpf->entity.index = index;
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 09/19] media: vsp1: drm: Split RPF format setting to separate function
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (7 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 08/19] media: vsp1: wpf: Add writeback support Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 10/19] media: vsp1: drm: Extend frame completion API to the DU driver Laurent Pinchart
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The code that initializes the RPF format-related fields for display
pipelines will also be useful for the WPF to implement writeback
support. Split it from vsp1_du_atomic_update() to a new
vsp1_du_pipeline_set_rwpf_format() function.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_drm.c | 55 ++++++++++++++++----------
 1 file changed, 35 insertions(+), 20 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 89773d3a916c..a0569e0d0f9e 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -566,6 +566,36 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
 	vsp1_dl_list_commit(dl, dl_flags);
 }
 
+static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1,
+					    struct vsp1_rwpf *rwpf,
+					    u32 pixelformat, unsigned int pitch)
+{
+	const struct vsp1_format_info *fmtinfo;
+	unsigned int chroma_hsub;
+
+	fmtinfo = vsp1_get_format_info(vsp1, pixelformat);
+	if (!fmtinfo) {
+		dev_dbg(vsp1->dev, "Unsupported pixel format %08x for RPF\n",
+			pixelformat);
+		return -EINVAL;
+	}
+
+	/*
+	 * Only formats with three planes can affect the chroma planes pitch.
+	 * All formats with two planes have a horizontal subsampling value of 2,
+	 * but combine U and V in a single chroma plane, which thus results in
+	 * the luma plane and chroma plane having the same pitch.
+	 */
+	chroma_hsub = (fmtinfo->planes == 3) ? fmtinfo->hsub : 1;
+
+	rwpf->fmtinfo = fmtinfo;
+	rwpf->format.num_planes = fmtinfo->planes;
+	rwpf->format.plane_fmt[0].bytesperline = pitch;
+	rwpf->format.plane_fmt[1].bytesperline = pitch / chroma_hsub;
+
+	return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * DU Driver API
  */
@@ -773,9 +803,8 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
 {
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 	struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
-	const struct vsp1_format_info *fmtinfo;
-	unsigned int chroma_hsub;
 	struct vsp1_rwpf *rpf;
+	int ret;
 
 	if (rpf_index >= vsp1->info->rpf_count)
 		return -EINVAL;
@@ -808,25 +837,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
 	 * Store the format, stride, memory buffer address, crop and compose
 	 * rectangles and Z-order position and for the input.
 	 */
-	fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat);
-	if (!fmtinfo) {
-		dev_dbg(vsp1->dev, "Unsupported pixel format %08x for RPF\n",
-			cfg->pixelformat);
-		return -EINVAL;
-	}
+	ret = vsp1_du_pipeline_set_rwpf_format(vsp1, rpf, cfg->pixelformat,
+					       cfg->pitch);
+	if (ret < 0)
+		return ret;
 
-	/*
-	 * Only formats with three planes can affect the chroma planes pitch.
-	 * All formats with two planes have a horizontal subsampling value of 2,
-	 * but combine U and V in a single chroma plane, which thus results in
-	 * the luma plane and chroma plane having the same pitch.
-	 */
-	chroma_hsub = (fmtinfo->planes == 3) ? fmtinfo->hsub : 1;
-
-	rpf->fmtinfo = fmtinfo;
-	rpf->format.num_planes = fmtinfo->planes;
-	rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
-	rpf->format.plane_fmt[1].bytesperline = cfg->pitch / chroma_hsub;
 	rpf->alpha = cfg->alpha;
 
 	rpf->mem.addr[0] = cfg->mem[0];
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 10/19] media: vsp1: drm: Extend frame completion API to the DU driver
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (8 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 09/19] media: vsp1: drm: Split RPF format setting to separate function Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 11/19] media: vsp1: drm: Implement writeback support Laurent Pinchart
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The VSP1 driver will need to pass extra flags to the DU through the
frame completion API. Replace the completed bool flag by a bitmask to
support this.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/rcar_du_vsp.c  | 4 ++--
 drivers/media/platform/vsp1/vsp1_drm.c | 4 ++--
 drivers/media/platform/vsp1/vsp1_drm.h | 2 +-
 include/media/vsp1.h                   | 4 +++-
 4 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 76a39eee7c9c..28bfeb8c24fb 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -26,14 +26,14 @@
 #include "rcar_du_kms.h"
 #include "rcar_du_vsp.h"
 
-static void rcar_du_vsp_complete(void *private, bool completed, u32 crc)
+static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
 {
 	struct rcar_du_crtc *crtc = private;
 
 	if (crtc->vblank_enable)
 		drm_crtc_handle_vblank(&crtc->crtc);
 
-	if (completed)
+	if (status & VSP1_DU_STATUS_COMPLETE)
 		rcar_du_crtc_finish_page_flip(crtc);
 
 	drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index a0569e0d0f9e..e4c44d6d3116 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -34,14 +34,14 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
 				       unsigned int completion)
 {
 	struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
-	bool complete = completion & VSP1_DL_FRAME_END_COMPLETED;
 
 	if (drm_pipe->du_complete) {
 		struct vsp1_entity *uif = drm_pipe->uif;
+		unsigned int status = completion & VSP1_DU_STATUS_COMPLETE;
 		u32 crc;
 
 		crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0;
-		drm_pipe->du_complete(drm_pipe->du_private, complete, crc);
+		drm_pipe->du_complete(drm_pipe->du_private, status, crc);
 	}
 
 	if (completion & VSP1_DL_FRAME_END_INTERNAL) {
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index 8dfd274a59e2..e85ad4366fbb 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -42,7 +42,7 @@ struct vsp1_drm_pipeline {
 	struct vsp1_du_crc_config crc;
 
 	/* Frame synchronisation */
-	void (*du_complete)(void *data, bool completed, u32 crc);
+	void (*du_complete)(void *data, unsigned int status, u32 crc);
 	void *du_private;
 };
 
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index 1cf868360701..877496936487 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -17,6 +17,8 @@ struct device;
 
 int vsp1_du_init(struct device *dev);
 
+#define VSP1_DU_STATUS_COMPLETE		BIT(0)
+
 /**
  * struct vsp1_du_lif_config - VSP LIF configuration
  * @width: output frame width
@@ -32,7 +34,7 @@ struct vsp1_du_lif_config {
 	unsigned int height;
 	bool interlaced;
 
-	void (*callback)(void *data, bool completed, u32 crc);
+	void (*callback)(void *data, unsigned int status, u32 crc);
 	void *callback_data;
 };
 
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 11/19] media: vsp1: drm: Implement writeback support
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (9 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 10/19] media: vsp1: drm: Extend frame completion API to the DU driver Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job Laurent Pinchart
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

Extend the vsp1_du_atomic_flush() API with writeback support by adding
format, pitch and memory addresses of the writeback framebuffer.
Writeback completion is reported through the existing frame completion
callback with a new VSP1_DU_STATUS_WRITEBACK status flag.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1_dl.c  | 14 ++++++++++++++
 drivers/media/platform/vsp1/vsp1_dl.h  |  3 ++-
 drivers/media/platform/vsp1/vsp1_drm.c | 25 ++++++++++++++++++++++++-
 include/media/vsp1.h                   | 15 +++++++++++++++
 4 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 7b4d252bfde7..dad7fa548642 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -1025,6 +1025,9 @@ void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
  *
  * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just
  * became active had been queued with the internal notification flag.
+ *
+ * The VSP1_DL_FRAME_END_WRITEBACK flag indicates that the previously active
+ * display list had been queued with the writeback flag.
  */
 unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
 {
@@ -1062,6 +1065,17 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
 	if (status & VI6_STATUS_FLD_STD(dlm->index))
 		goto done;
 
+	/*
+	 * If the active display list has the writeback flag set, the frame
+	 * completion marks the end of the writeback capture. Return the
+	 * VSP1_DL_FRAME_END_WRITEBACK flag and reset the display list's
+	 * writeback flag.
+	 */
+	if (dlm->active && (dlm->active->flags & VSP1_DL_FRAME_END_WRITEBACK)) {
+		flags |= VSP1_DL_FRAME_END_WRITEBACK;
+		dlm->active->flags &= ~VSP1_DL_FRAME_END_WRITEBACK;
+	}
+
 	/*
 	 * The device starts processing the queued display list right after the
 	 * frame end interrupt. The display list thus becomes active.
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index f845607abc4c..3a34b37df79a 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -18,7 +18,8 @@ struct vsp1_dl_list;
 struct vsp1_dl_manager;
 
 #define VSP1_DL_FRAME_END_COMPLETED		BIT(0)
-#define VSP1_DL_FRAME_END_INTERNAL		BIT(1)
+#define VSP1_DL_FRAME_END_WRITEBACK		BIT(1)
+#define VSP1_DL_FRAME_END_INTERNAL		BIT(2)
 
 /**
  * struct vsp1_dl_ext_cmd - Extended Display command
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index e4c44d6d3116..f1d8e2039ce8 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -37,7 +37,9 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
 
 	if (drm_pipe->du_complete) {
 		struct vsp1_entity *uif = drm_pipe->uif;
-		unsigned int status = completion & VSP1_DU_STATUS_COMPLETE;
+		unsigned int status = completion
+				    & (VSP1_DU_STATUS_COMPLETE |
+				       VSP1_DU_STATUS_WRITEBACK);
 		u32 crc;
 
 		crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0;
@@ -541,6 +543,8 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
 
 	if (drm_pipe->force_brx_release)
 		dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
+	if (pipe->output->writeback)
+		dl_flags |= VSP1_DL_FRAME_END_WRITEBACK;
 
 	dl = vsp1_dl_list_get(pipe->output->dlm);
 	dlb = vsp1_dl_list_get_body0(dl);
@@ -870,12 +874,31 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 	struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
 	struct vsp1_pipeline *pipe = &drm_pipe->pipe;
+	int ret;
 
 	drm_pipe->crc = cfg->crc;
 
 	mutex_lock(&vsp1->drm->lock);
+
+	if (pipe->output->has_writeback && cfg->writeback.pixelformat) {
+		const struct vsp1_du_writeback_config *wb_cfg = &cfg->writeback;
+
+		ret = vsp1_du_pipeline_set_rwpf_format(vsp1, pipe->output,
+						       wb_cfg->pixelformat,
+						       wb_cfg->pitch);
+		if (WARN_ON(ret < 0))
+			goto done;
+
+		pipe->output->mem.addr[0] = wb_cfg->mem[0];
+		pipe->output->mem.addr[1] = wb_cfg->mem[1];
+		pipe->output->mem.addr[2] = wb_cfg->mem[2];
+		pipe->output->writeback = true;
+	}
+
 	vsp1_du_pipeline_setup_inputs(vsp1, pipe);
 	vsp1_du_pipeline_configure(pipe);
+
+done:
 	mutex_unlock(&vsp1->drm->lock);
 }
 EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index 877496936487..cc1b0d42ce95 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -18,6 +18,7 @@ struct device;
 int vsp1_du_init(struct device *dev);
 
 #define VSP1_DU_STATUS_COMPLETE		BIT(0)
+#define VSP1_DU_STATUS_WRITEBACK	BIT(1)
 
 /**
  * struct vsp1_du_lif_config - VSP LIF configuration
@@ -83,12 +84,26 @@ struct vsp1_du_crc_config {
 	unsigned int index;
 };
 
+/**
+ * struct vsp1_du_writeback_config - VSP writeback configuration parameters
+ * @pixelformat: plane pixel format (V4L2 4CC)
+ * @pitch: line pitch in bytes for the first plane
+ * @mem: DMA memory address for each plane of the frame buffer
+ */
+struct vsp1_du_writeback_config {
+	u32 pixelformat;
+	unsigned int pitch;
+	dma_addr_t mem[3];
+};
+
 /**
  * struct vsp1_du_atomic_pipe_config - VSP atomic pipe configuration parameters
  * @crc: CRC computation configuration
+ * @writeback: writeback configuration
  */
 struct vsp1_du_atomic_pipe_config {
 	struct vsp1_du_crc_config crc;
+	struct vsp1_du_writeback_config writeback;
 };
 
 void vsp1_du_atomic_begin(struct device *dev, unsigned int pipe_index);
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (10 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 11/19] media: vsp1: drm: Implement writeback support Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:42   ` Laurent Pinchart
                     ` (2 more replies)
  2019-02-21 10:32 ` [PATCH v5 13/19] drm: writeback: Fix leak of writeback job Laurent Pinchart
                   ` (7 subsequent siblings)
  19 siblings, 3 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The drm_writeback_queue_job() function takes ownership of the passed job
and requires the caller to manually set the connector state
writeback_job pointer to NULL. To simplify drivers and avoid errors
(such as the missing NULL set in the vc4 driver), pass the connector
state pointer to the function instead of the job pointer, and set the
writeback_job pointer to NULL internally.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/arm/malidp_mw.c |  3 +--
 drivers/gpu/drm/drm_writeback.c | 15 ++++++++++-----
 drivers/gpu/drm/vc4/vc4_txp.c   |  2 +-
 include/drm/drm_writeback.h     |  2 +-
 4 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
index 041a64dc7167..87627219ce3b 100644
--- a/drivers/gpu/drm/arm/malidp_mw.c
+++ b/drivers/gpu/drm/arm/malidp_mw.c
@@ -252,8 +252,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm,
 				     &mw_state->addrs[0],
 				     mw_state->format);
 
-		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
-		conn_state->writeback_job = NULL;
+		drm_writeback_queue_job(mw_conn, conn_state);
 		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
 					   mw_state->pitches, mw_state->n_planes,
 					   fb->width, fb->height, mw_state->format,
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index c20e6fe00cb3..338b993d7c9f 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -242,11 +242,12 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
 /**
  * drm_writeback_queue_job - Queue a writeback job for later signalling
  * @wb_connector: The writeback connector to queue a job on
- * @job: The job to queue
+ * @conn_state: The connector state containing the job to queue
  *
- * This function adds a job to the job_queue for a writeback connector. It
- * should be considered to take ownership of the writeback job, and so any other
- * references to the job must be cleared after calling this function.
+ * This function adds the job contained in @conn_state to the job_queue for a
+ * writeback connector. It takes ownership of the writeback job and sets the
+ * @conn_state->writeback_job to NULL, and so no access to the job may be
+ * performed by the caller after this function returns.
  *
  * Drivers must ensure that for a given writeback connector, jobs are queued in
  * exactly the same order as they will be completed by the hardware (and
@@ -258,10 +259,14 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
  * See also: drm_writeback_signal_completion()
  */
 void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
-			     struct drm_writeback_job *job)
+			     struct drm_connector_state *conn_state)
 {
+	struct drm_writeback_job *job;
 	unsigned long flags;
 
+	job = conn_state->writeback_job;
+	conn_state->writeback_job = NULL;
+
 	spin_lock_irqsave(&wb_connector->job_lock, flags);
 	list_add_tail(&job->list_entry, &wb_connector->job_queue);
 	spin_unlock_irqrestore(&wb_connector->job_lock, flags);
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index aa279b5b0de7..5dabd91f2d7e 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -327,7 +327,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
 
 	TXP_WRITE(TXP_DST_CTRL, ctrl);
 
-	drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
+	drm_writeback_queue_job(&txp->connector, conn_state);
 }
 
 static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
index 23df9d463003..47662c362743 100644
--- a/include/drm/drm_writeback.h
+++ b/include/drm/drm_writeback.h
@@ -123,7 +123,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 const u32 *formats, int n_formats);
 
 void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
-			     struct drm_writeback_job *job);
+			     struct drm_connector_state *conn_state);
 
 void drm_writeback_cleanup_job(struct drm_writeback_job *job);
 
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 13/19] drm: writeback: Fix leak of writeback job
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (11 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 17:48   ` Brian Starkey
  2019-02-26 18:10   ` Liviu Dudau
  2019-02-21 10:32 ` [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations Laurent Pinchart
                   ` (6 subsequent siblings)
  19 siblings, 2 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

Writeback jobs are allocated when the WRITEBACK_FB_ID is set, and
deleted when the jobs complete. This results in both a memory leak of
the job and a leak of the framebuffer if the atomic commit returns
before the job is queued for processing, for instance if the atomic
check fails or if the commit runs in test-only mode.

Fix this by implementing the drm_writeback_cleanup_job() function and
calling it from __drm_atomic_helper_connector_destroy_state(). As
writeback jobs are removed from the state when they're queued for
processing, any job left in the state when the state gets destroyed
needs to be cleaned up.

The existing declaration of the drm_writeback_cleanup_job() function
without an implementation hints that this problem was considered, but
never addressed.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/drm_atomic_state_helper.c |  4 ++++
 drivers/gpu/drm/drm_writeback.c           | 13 ++++++++++---
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 4985384e51f6..59ffb6b9c745 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -30,6 +30,7 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_device.h>
+#include <drm/drm_writeback.h>
 
 #include <linux/slab.h>
 #include <linux/dma-fence.h>
@@ -412,6 +413,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
 
 	if (state->commit)
 		drm_crtc_commit_put(state->commit);
+
+	if (state->writeback_job)
+		drm_writeback_cleanup_job(state->writeback_job);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
 
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index 338b993d7c9f..afb1ae6e0ecb 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -273,6 +273,14 @@ void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
 }
 EXPORT_SYMBOL(drm_writeback_queue_job);
 
+void drm_writeback_cleanup_job(struct drm_writeback_job *job)
+{
+	if (job->fb)
+		drm_framebuffer_put(job->fb);
+
+	kfree(job);
+}
+
 /*
  * @cleanup_work: deferred cleanup of a writeback job
  *
@@ -285,10 +293,9 @@ static void cleanup_work(struct work_struct *work)
 	struct drm_writeback_job *job = container_of(work,
 						     struct drm_writeback_job,
 						     cleanup_work);
-	drm_framebuffer_put(job->fb);
-	kfree(job);
-}
 
+	drm_writeback_cleanup_job(job);
+}
 
 /**
  * drm_writeback_signal_completion - Signal the completion of a writeback job
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (12 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 13/19] drm: writeback: Fix leak of writeback job Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 18:12   ` Brian Starkey
  2019-02-26 18:39   ` Liviu Dudau
  2019-02-21 10:32 ` [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions Laurent Pinchart
                   ` (5 subsequent siblings)
  19 siblings, 2 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

As writeback jobs contain a framebuffer, drivers may need to prepare and
cleanup them the same way they can prepare and cleanup framebuffers for
planes. Add two new optional connector helper operations,
.prepare_writeback_job() and .cleanup_writeback_job() to support this.

The job prepare operation is called from
drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
that would need to be called by all drivers not using
drm_atomic_helper_commit(). The job cleanup operation is called from the
existing drm_writeback_cleanup_job() function, invoked both when
destroying the job as part of a aborted commit, or when the job
completes.

The drm_writeback_job structure is extended with a priv field to let
drivers store per-job data, such as mappings related to the writeback
framebuffer.

For internal plumbing reasons the drm_writeback_job structure needs to
store a back-pointer to the drm_writeback_connector. To avoid pushing
too much writeback-specific knowledge to drm_atomic_uapi.c, create a
drm_writeback_set_fb() function, move the writeback job setup code
there, and set the connector backpointer. The prepare_signaling()
function doesn't need to allocate writeback jobs and can ignore
connectors without a job, as it is called after the writeback jobs are
allocated to store framebuffers, and a writeback fence with a
framebuffer is an invalid configuration that gets rejected by the commit
check.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
 drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
 drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
 include/drm/drm_modeset_helper_vtables.h |  7 ++++
 include/drm/drm_writeback.h              | 28 ++++++++++++++-
 5 files changed, 96 insertions(+), 24 deletions(-)

This patch is currently missing documentation for the
.prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
to fix this, but first wanted feedback on the direction taken. I'm not
entirely happy with the priv pointer in the drm_writeback_job structure,
but adding a full state duplicate/destroy machinery for that structure
was equally unappealing to me.

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 6fe2303fccd9..70a4886c6e65 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
 int drm_atomic_helper_prepare_planes(struct drm_device *dev,
 				     struct drm_atomic_state *state)
 {
+	struct drm_connector *connector;
+	struct drm_connector_state *new_conn_state;
 	struct drm_plane *plane;
 	struct drm_plane_state *new_plane_state;
 	int ret, i, j;
 
+	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
+		if (!new_conn_state->writeback_job)
+			continue;
+
+		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
+		if (ret < 0)
+			return ret;
+	}
+
 	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
 		const struct drm_plane_helper_funcs *funcs;
 
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index c40889888a16..e802152a01ad 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
 	return 0;
 }
 
-static struct drm_writeback_job *
-drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
-{
-	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
-
-	if (!conn_state->writeback_job)
-		conn_state->writeback_job =
-			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
-
-	return conn_state->writeback_job;
-}
-
 static int drm_atomic_set_writeback_fb_for_connector(
 		struct drm_connector_state *conn_state,
 		struct drm_framebuffer *fb)
 {
-	struct drm_writeback_job *job =
-		drm_atomic_get_writeback_job(conn_state);
-	if (!job)
-		return -ENOMEM;
+	int ret;
 
-	drm_framebuffer_assign(&job->fb, fb);
+	ret = drm_writeback_set_fb(conn_state, fb);
+	if (ret < 0)
+		return ret;
 
 	if (fb)
 		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
@@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
 
 	for_each_new_connector_in_state(state, conn, conn_state, i) {
 		struct drm_writeback_connector *wb_conn;
-		struct drm_writeback_job *job;
 		struct drm_out_fence_state *f;
 		struct dma_fence *fence;
 		s32 __user *fence_ptr;
 
+		if (!conn_state->writeback_job)
+			continue;
+
 		fence_ptr = get_out_fence_for_connector(state, conn);
 		if (!fence_ptr)
 			continue;
 
-		job = drm_atomic_get_writeback_job(conn_state);
-		if (!job)
-			return -ENOMEM;
-
 		f = krealloc(*fence_state, sizeof(**fence_state) *
 			     (*num_fences + 1), GFP_KERNEL);
 		if (!f)
@@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
 			return ret;
 		}
 
-		job->out_fence = fence;
+		conn_state->writeback_job->out_fence = fence;
 	}
 
 	/*
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index afb1ae6e0ecb..4678d61d634a 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_writeback_connector_init);
 
+int drm_writeback_set_fb(struct drm_connector_state *conn_state,
+			 struct drm_framebuffer *fb)
+{
+	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
+
+	if (!conn_state->writeback_job) {
+		conn_state->writeback_job =
+			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
+		if (!conn_state->writeback_job)
+			return -ENOMEM;
+
+		conn_state->writeback_job->connector =
+			drm_connector_to_writeback(conn_state->connector);
+	}
+
+	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
+	return 0;
+}
+
+int drm_writeback_prepare_job(struct drm_writeback_job *job)
+{
+	struct drm_writeback_connector *connector = job->connector;
+	const struct drm_connector_helper_funcs *funcs =
+		connector->base.helper_private;
+	int ret;
+
+	if (funcs->cleanup_writeback_job) {
+		ret = funcs->prepare_writeback_job(connector, job);
+		if (ret < 0)
+			return ret;
+	}
+
+	job->prepared = true;
+	return 0;
+}
+
 /**
  * drm_writeback_queue_job - Queue a writeback job for later signalling
  * @wb_connector: The writeback connector to queue a job on
@@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
 
 void drm_writeback_cleanup_job(struct drm_writeback_job *job)
 {
+	struct drm_writeback_connector *connector = job->connector;
+	const struct drm_connector_helper_funcs *funcs =
+		connector->base.helper_private;
+
+	if (job->prepared && funcs->cleanup_writeback_job)
+		funcs->cleanup_writeback_job(connector, job);
+
 	if (job->fb)
 		drm_framebuffer_put(job->fb);
 
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
index 61142aa0ab23..73d03fe66799 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -49,6 +49,8 @@
  */
 
 enum mode_set_atomic;
+struct drm_writeback_connector;
+struct drm_writeback_job;
 
 /**
  * struct drm_crtc_helper_funcs - helper operations for CRTCs
@@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
 	 */
 	void (*atomic_commit)(struct drm_connector *connector,
 			      struct drm_connector_state *state);
+
+	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
+				     struct drm_writeback_job *job);
+	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
+				      struct drm_writeback_job *job);
 };
 
 /**
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
index 47662c362743..777c14c847f0 100644
--- a/include/drm/drm_writeback.h
+++ b/include/drm/drm_writeback.h
@@ -79,6 +79,20 @@ struct drm_writeback_connector {
 };
 
 struct drm_writeback_job {
+	/**
+	 * @connector:
+	 *
+	 * Back-pointer to the writeback connector associated with the job
+	 */
+	struct drm_writeback_connector *connector;
+
+	/**
+	 * @prepared:
+	 *
+	 * Set when the job has been prepared with drm_writeback_prepare_job()
+	 */
+	bool prepared;
+
 	/**
 	 * @cleanup_work:
 	 *
@@ -98,7 +112,7 @@ struct drm_writeback_job {
 	 * @fb:
 	 *
 	 * Framebuffer to be written to by the writeback connector. Do not set
-	 * directly, use drm_atomic_set_writeback_fb_for_connector()
+	 * directly, use drm_writeback_set_fb()
 	 */
 	struct drm_framebuffer *fb;
 
@@ -108,6 +122,13 @@ struct drm_writeback_job {
 	 * Fence which will signal once the writeback has completed
 	 */
 	struct dma_fence *out_fence;
+
+	/**
+	 * @priv:
+	 *
+	 * Driver-private data
+	 */
+	void *priv;
 };
 
 static inline struct drm_writeback_connector *
@@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
 				 const u32 *formats, int n_formats);
 
+int drm_writeback_set_fb(struct drm_connector_state *conn_state,
+			 struct drm_framebuffer *fb);
+
+int drm_writeback_prepare_job(struct drm_writeback_job *job);
+
 void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
 			     struct drm_connector_state *conn_state);
 
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (13 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:39   ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation Laurent Pinchart
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The msm_atomic_state_clear() and msm_atomic_state_free() functions are
declared but never defined. Remove their prototypes.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/msm/msm_drv.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 4e0c6c2f9a86..8f0287e75efb 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -240,8 +240,6 @@ int msm_atomic_prepare_fb(struct drm_plane *plane,
 			  struct drm_plane_state *new_state);
 void msm_atomic_commit_tail(struct drm_atomic_state *state);
 struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
-void msm_atomic_state_clear(struct drm_atomic_state *state);
-void msm_atomic_state_free(struct drm_atomic_state *state);
 
 int msm_gem_init_vma(struct msm_gem_address_space *aspace,
 		struct msm_gem_vma *vma, int npages);
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (14 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-03-11 22:57   ` Kieran Bingham
  2019-02-21 10:32 ` [PATCH v5 17/19] drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure Laurent Pinchart
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The rcar_du_crtc structure index field contains the CRTC hardware index,
not the hardware and software index. Update the documentation
accordingly.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index bcb35b0b7612..c478953be092 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -27,7 +27,7 @@ struct rcar_du_vsp;
  * @clock: the CRTC functional clock
  * @extclock: external pixel dot clock (optional)
  * @mmio_offset: offset of the CRTC registers in the DU MMIO block
- * @index: CRTC software and hardware index
+ * @index: CRTC hardware index
  * @initialized: whether the CRTC has been initialized and clocks enabled
  * @dsysr: cached value of the DSYSR register
  * @vblank_enable: whether vblank events are enabled on this CRTC
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 17/19] drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (15 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-03-11 23:20   ` Kieran Bingham
  2019-02-21 10:32 ` [PATCH v5 18/19] drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate functions Laurent Pinchart
                   ` (2 subsequent siblings)
  19 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The mapping between DRM and V4L2 fourcc's is stored in two separate
tables in rcar_du_vsp.c. In order to make it reusable to implement
writeback support, move it to the rcar_du_format_info structure.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/rcar_du_kms.c | 25 +++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_kms.h |  1 +
 drivers/gpu/drm/rcar-du/rcar_du_vsp.c | 44 ++++-----------------------
 3 files changed, 32 insertions(+), 38 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index b0c80dffd8b8..999440c7b258 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -32,60 +32,70 @@
 static const struct rcar_du_format_info rcar_du_format_infos[] = {
 	{
 		.fourcc = DRM_FORMAT_RGB565,
+		.v4l2 = V4L2_PIX_FMT_RGB565,
 		.bpp = 16,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_ARGB1555,
+		.v4l2 = V4L2_PIX_FMT_ARGB555,
 		.bpp = 16,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_XRGB1555,
+		.v4l2 = V4L2_PIX_FMT_XRGB555,
 		.bpp = 16,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_XRGB8888,
+		.v4l2 = V4L2_PIX_FMT_XBGR32,
 		.bpp = 32,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
 		.edf = PnDDCR4_EDF_RGB888,
 	}, {
 		.fourcc = DRM_FORMAT_ARGB8888,
+		.v4l2 = V4L2_PIX_FMT_ABGR32,
 		.bpp = 32,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
 		.edf = PnDDCR4_EDF_ARGB8888,
 	}, {
 		.fourcc = DRM_FORMAT_UYVY,
+		.v4l2 = V4L2_PIX_FMT_UYVY,
 		.bpp = 16,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_YUYV,
+		.v4l2 = V4L2_PIX_FMT_YUYV,
 		.bpp = 16,
 		.planes = 1,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_NV12,
+		.v4l2 = V4L2_PIX_FMT_NV12M,
 		.bpp = 12,
 		.planes = 2,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_NV21,
+		.v4l2 = V4L2_PIX_FMT_NV21M,
 		.bpp = 12,
 		.planes = 2,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
 		.edf = PnDDCR4_EDF_NONE,
 	}, {
 		.fourcc = DRM_FORMAT_NV16,
+		.v4l2 = V4L2_PIX_FMT_NV16M,
 		.bpp = 16,
 		.planes = 2,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
@@ -97,62 +107,77 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
 	 */
 	{
 		.fourcc = DRM_FORMAT_RGB332,
+		.v4l2 = V4L2_PIX_FMT_RGB332,
 		.bpp = 8,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_ARGB4444,
+		.v4l2 = V4L2_PIX_FMT_ARGB444,
 		.bpp = 16,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_XRGB4444,
+		.v4l2 = V4L2_PIX_FMT_XRGB444,
 		.bpp = 16,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_BGR888,
+		.v4l2 = V4L2_PIX_FMT_RGB24,
 		.bpp = 24,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_RGB888,
+		.v4l2 = V4L2_PIX_FMT_BGR24,
 		.bpp = 24,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_BGRA8888,
+		.v4l2 = V4L2_PIX_FMT_ARGB32,
 		.bpp = 32,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_BGRX8888,
+		.v4l2 = V4L2_PIX_FMT_XRGB32,
 		.bpp = 32,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_YVYU,
+		.v4l2 = V4L2_PIX_FMT_YVYU,
 		.bpp = 16,
 		.planes = 1,
 	}, {
 		.fourcc = DRM_FORMAT_NV61,
+		.v4l2 = V4L2_PIX_FMT_NV61M,
 		.bpp = 16,
 		.planes = 2,
 	}, {
 		.fourcc = DRM_FORMAT_YUV420,
+		.v4l2 = V4L2_PIX_FMT_YUV420M,
 		.bpp = 12,
 		.planes = 3,
 	}, {
 		.fourcc = DRM_FORMAT_YVU420,
+		.v4l2 = V4L2_PIX_FMT_YVU420M,
 		.bpp = 12,
 		.planes = 3,
 	}, {
 		.fourcc = DRM_FORMAT_YUV422,
+		.v4l2 = V4L2_PIX_FMT_YUV422M,
 		.bpp = 16,
 		.planes = 3,
 	}, {
 		.fourcc = DRM_FORMAT_YVU422,
+		.v4l2 = V4L2_PIX_FMT_YVU422M,
 		.bpp = 16,
 		.planes = 3,
 	}, {
 		.fourcc = DRM_FORMAT_YUV444,
+		.v4l2 = V4L2_PIX_FMT_YUV444M,
 		.bpp = 24,
 		.planes = 3,
 	}, {
 		.fourcc = DRM_FORMAT_YVU444,
+		.v4l2 = V4L2_PIX_FMT_YVU444M,
 		.bpp = 24,
 		.planes = 3,
 	},
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
index e171527abdaa..0346504d8c59 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -19,6 +19,7 @@ struct rcar_du_device;
 
 struct rcar_du_format_info {
 	u32 fourcc;
+	u32 v4l2;
 	unsigned int bpp;
 	unsigned int planes;
 	unsigned int pnmr;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 28bfeb8c24fb..29a08f7b0761 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -109,8 +109,7 @@ void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
 	vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
 }
 
-/* Keep the two tables in sync. */
-static const u32 formats_kms[] = {
+static const u32 rcar_du_vsp_formats[] = {
 	DRM_FORMAT_RGB332,
 	DRM_FORMAT_ARGB4444,
 	DRM_FORMAT_XRGB4444,
@@ -138,40 +137,13 @@ static const u32 formats_kms[] = {
 	DRM_FORMAT_YVU444,
 };
 
-static const u32 formats_v4l2[] = {
-	V4L2_PIX_FMT_RGB332,
-	V4L2_PIX_FMT_ARGB444,
-	V4L2_PIX_FMT_XRGB444,
-	V4L2_PIX_FMT_ARGB555,
-	V4L2_PIX_FMT_XRGB555,
-	V4L2_PIX_FMT_RGB565,
-	V4L2_PIX_FMT_RGB24,
-	V4L2_PIX_FMT_BGR24,
-	V4L2_PIX_FMT_ARGB32,
-	V4L2_PIX_FMT_XRGB32,
-	V4L2_PIX_FMT_ABGR32,
-	V4L2_PIX_FMT_XBGR32,
-	V4L2_PIX_FMT_UYVY,
-	V4L2_PIX_FMT_YUYV,
-	V4L2_PIX_FMT_YVYU,
-	V4L2_PIX_FMT_NV12M,
-	V4L2_PIX_FMT_NV21M,
-	V4L2_PIX_FMT_NV16M,
-	V4L2_PIX_FMT_NV61M,
-	V4L2_PIX_FMT_YUV420M,
-	V4L2_PIX_FMT_YVU420M,
-	V4L2_PIX_FMT_YUV422M,
-	V4L2_PIX_FMT_YVU422M,
-	V4L2_PIX_FMT_YUV444M,
-	V4L2_PIX_FMT_YVU444M,
-};
-
 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 {
 	struct rcar_du_vsp_plane_state *state =
 		to_rcar_vsp_plane_state(plane->plane.state);
 	struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
 	struct drm_framebuffer *fb = plane->plane.state->fb;
+	const struct rcar_du_format_info *format;
 	struct vsp1_du_atomic_config cfg = {
 		.pixelformat = 0,
 		.pitch = fb->pitches[0],
@@ -194,12 +166,8 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 		cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
 			   + fb->offsets[i];
 
-	for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
-		if (formats_kms[i] == state->format->fourcc) {
-			cfg.pixelformat = formats_v4l2[i];
-			break;
-		}
-	}
+	format = rcar_du_format_info(state->format->fourcc);
+	cfg.pixelformat = format->v4l2;
 
 	vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
 			      plane->index, &cfg);
@@ -394,8 +362,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
 
 		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
 					       &rcar_du_vsp_plane_funcs,
-					       formats_kms,
-					       ARRAY_SIZE(formats_kms),
+					       rcar_du_vsp_formats,
+					       ARRAY_SIZE(rcar_du_vsp_formats),
 					       NULL, type, NULL);
 		if (ret < 0)
 			return ret;
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 18/19] drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate functions
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (16 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 17/19] drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-21 10:32 ` [PATCH v5 19/19] drm: rcar-du: Add writeback support for R-Car Gen3 Laurent Pinchart
  2019-02-22 14:04 ` [PATCH v5 00/19] R-Car DU display writeback support Brian Starkey
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

The rcar_du_vsp_plane_prepare_fb() and rcar_du_vsp_plane_cleanup_fb()
functions implement the DRM plane .prepare_fb() and .cleanup_fb()
operations. They map and unmap the framebuffer to/from the VSP
internally, which will be useful to implement writeback support. Split
the mapping and unmapping out to separate functions.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/rcar_du_vsp.c | 68 ++++++++++++++++-----------
 drivers/gpu/drm/rcar-du/rcar_du_vsp.h | 17 +++++++
 2 files changed, 58 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 29a08f7b0761..0806a69c4679 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -173,26 +173,16 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 			      plane->index, &cfg);
 }
 
-static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
-					struct drm_plane_state *state)
+int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+		       struct sg_table sg_tables[3])
 {
-	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
-	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
 	struct rcar_du_device *rcdu = vsp->dev;
 	unsigned int i;
 	int ret;
 
-	/*
-	 * There's no need to prepare (and unprepare) the framebuffer when the
-	 * plane is not visible, as it will not be displayed.
-	 */
-	if (!state->visible)
-		return 0;
-
-	for (i = 0; i < rstate->format->planes; ++i) {
-		struct drm_gem_cma_object *gem =
-			drm_fb_cma_get_gem_obj(state->fb, i);
-		struct sg_table *sgt = &rstate->sg_tables[i];
+	for (i = 0; i < fb->format->num_planes; ++i) {
+		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
+		struct sg_table *sgt = &sg_tables[i];
 
 		ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
 				      gem->base.size);
@@ -207,15 +197,11 @@ static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
 		}
 	}
 
-	ret = drm_gem_fb_prepare_fb(plane, state);
-	if (ret)
-		goto fail;
-
 	return 0;
 
 fail:
 	while (i--) {
-		struct sg_table *sgt = &rstate->sg_tables[i];
+		struct sg_table *sgt = &sg_tables[i];
 
 		vsp1_du_unmap_sg(vsp->vsp, sgt);
 		sg_free_table(sgt);
@@ -224,22 +210,50 @@ static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
 	return ret;
 }
 
+static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
+					struct drm_plane_state *state)
+{
+	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
+	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
+	int ret;
+
+	/*
+	 * There's no need to prepare (and unprepare) the framebuffer when the
+	 * plane is not visible, as it will not be displayed.
+	 */
+	if (!state->visible)
+		return 0;
+
+	ret = rcar_du_vsp_map_fb(vsp, state->fb, rstate->sg_tables);
+	if (ret < 0)
+		return ret;
+
+	return drm_gem_fb_prepare_fb(plane, state);
+}
+
+void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+			  struct sg_table sg_tables[3])
+{
+	unsigned int i;
+
+	for (i = 0; i < fb->format->num_planes; ++i) {
+		struct sg_table *sgt = &sg_tables[i];
+
+		vsp1_du_unmap_sg(vsp->vsp, sgt);
+		sg_free_table(sgt);
+	}
+}
+
 static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
 					 struct drm_plane_state *state)
 {
 	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
 	struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
-	unsigned int i;
 
 	if (!state->visible)
 		return;
 
-	for (i = 0; i < rstate->format->planes; ++i) {
-		struct sg_table *sgt = &rstate->sg_tables[i];
-
-		vsp1_du_unmap_sg(vsp->vsp, sgt);
-		sg_free_table(sgt);
-	}
+	rcar_du_vsp_unmap_fb(vsp, state->fb, rstate->sg_tables);
 }
 
 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
index db232037f24a..9b4724159378 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
@@ -12,8 +12,10 @@
 
 #include <drm/drm_plane.h>
 
+struct drm_framebuffer;
 struct rcar_du_format_info;
 struct rcar_du_vsp;
+struct sg_table;
 
 struct rcar_du_vsp_plane {
 	struct drm_plane plane;
@@ -60,6 +62,10 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc);
 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
 void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
+int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+		       struct sg_table sg_tables[3]);
+void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+			  struct sg_table sg_tables[3]);
 #else
 static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp,
 				   struct device_node *np,
@@ -71,6 +77,17 @@ static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { };
 static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
 static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
 static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { };
+static inline int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp,
+				     struct drm_framebuffer *fb,
+				     struct sg_table sg_tables[3])
+{
+	return -ENXIO;
+}
+static inline void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp,
+					struct drm_framebuffer *fb,
+					struct sg_table sg_tables[3])
+{
+}
 #endif
 
 #endif /* __RCAR_DU_VSP_H__ */
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH v5 19/19] drm: rcar-du: Add writeback support for R-Car Gen3
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (17 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 18/19] drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate functions Laurent Pinchart
@ 2019-02-21 10:32 ` Laurent Pinchart
  2019-02-22 14:04 ` [PATCH v5 00/19] R-Car DU display writeback support Brian Starkey
  19 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:32 UTC (permalink / raw)
  To: dri-devel; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham

Implement writeback support for R-Car Gen3 by exposing writeback
connectors. Behind the scene the calls are forwarded to the VSP
backend.

Using writeback connectors will allow implemented writeback support for
R-Car Gen2 with a consistent API if desired.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/Kconfig             |   4 +
 drivers/gpu/drm/rcar-du/Makefile            |   3 +-
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h      |   7 +-
 drivers/gpu/drm/rcar-du/rcar_du_kms.c       |  12 ++
 drivers/gpu/drm/rcar-du/rcar_du_vsp.c       |   5 +
 drivers/gpu/drm/rcar-du/rcar_du_writeback.c | 203 ++++++++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_writeback.h |  39 ++++
 7 files changed, 271 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.c
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.h

diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 7c36e2777a15..1529849e217e 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -36,3 +36,7 @@ config DRM_RCAR_VSP
 	depends on VIDEO_RENESAS_VSP1=y || (VIDEO_RENESAS_VSP1 && DRM_RCAR_DU=m)
 	help
 	  Enable support to expose the R-Car VSP Compositor as KMS planes.
+
+config DRM_RCAR_WRITEBACK
+	bool
+	default y if ARM64
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 2a3b8d7972b5..6c2ed9c46467 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -4,7 +4,7 @@ rcar-du-drm-y := rcar_du_crtc.o \
 		 rcar_du_encoder.o \
 		 rcar_du_group.o \
 		 rcar_du_kms.o \
-		 rcar_du_plane.o
+		 rcar_du_plane.o \
 
 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_of.o \
 					   rcar_du_of_lvds_r8a7790.dtb.o \
@@ -13,6 +13,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_of.o \
 					   rcar_du_of_lvds_r8a7795.dtb.o \
 					   rcar_du_of_lvds_r8a7796.dtb.o
 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)	+= rcar_du_vsp.o
+rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
 
 obj-$(CONFIG_DRM_RCAR_DU)		+= rcar-du-drm.o
 obj-$(CONFIG_DRM_RCAR_DW_HDMI)		+= rcar_dw_hdmi.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index c478953be092..92f7d5f3ff80 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -15,6 +15,7 @@
 #include <linux/wait.h>
 
 #include <drm/drm_crtc.h>
+#include <drm/drm_writeback.h>
 
 #include <media/vsp1.h>
 
@@ -39,6 +40,7 @@ struct rcar_du_vsp;
  * @group: CRTC group this CRTC belongs to
  * @vsp: VSP feeding video to this CRTC
  * @vsp_pipe: index of the VSP pipeline feeding video to this CRTC
+ * @writeback: the writeback connector
  */
 struct rcar_du_crtc {
 	struct drm_crtc crtc;
@@ -65,9 +67,12 @@ struct rcar_du_crtc {
 
 	const char *const *sources;
 	unsigned int sources_count;
+
+	struct drm_writeback_connector writeback;
 };
 
-#define to_rcar_crtc(c)	container_of(c, struct rcar_du_crtc, crtc)
+#define to_rcar_crtc(c)		container_of(c, struct rcar_du_crtc, crtc)
+#define wb_to_rcar_crtc(c)	container_of(c, struct rcar_du_crtc, writeback)
 
 /**
  * struct rcar_du_crtc_state - Driver-specific CRTC state
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 999440c7b258..c729f048626e 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -24,6 +24,7 @@
 #include "rcar_du_kms.h"
 #include "rcar_du_regs.h"
 #include "rcar_du_vsp.h"
+#include "rcar_du_writeback.h"
 
 /* -----------------------------------------------------------------------------
  * Format helpers
@@ -662,6 +663,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 		encoder->possible_clones = (1 << num_encoders) - 1;
 	}
 
+	/* Create the writeback connectors. */
+	if (rcdu->info->gen >= 3) {
+		for (i = 0; i < rcdu->num_crtcs; ++i) {
+			struct rcar_du_crtc *rcrtc = &rcdu->crtcs[i];
+
+			ret = rcar_du_writeback_init(rcdu, rcrtc);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
 	/*
 	 * Initialize the default DPAD0 source to the index of the first DU
 	 * channel that can be connected to DPAD0. The exact value doesn't
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 0806a69c4679..99ae03a1713a 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -25,6 +25,7 @@
 #include "rcar_du_drv.h"
 #include "rcar_du_kms.h"
 #include "rcar_du_vsp.h"
+#include "rcar_du_writeback.h"
 
 static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
 {
@@ -35,6 +36,8 @@ static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
 
 	if (status & VSP1_DU_STATUS_COMPLETE)
 		rcar_du_crtc_finish_page_flip(crtc);
+	if (status & VSP1_DU_STATUS_WRITEBACK)
+		rcar_du_writeback_complete(crtc);
 
 	drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
 }
@@ -106,6 +109,8 @@ void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
 	state = to_rcar_crtc_state(crtc->crtc.state);
 	cfg.crc = state->crc;
 
+	rcar_du_writeback_atomic_flush(crtc, &cfg.writeback);
+
 	vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
 }
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c
new file mode 100644
index 000000000000..b5eea325a082
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rcar_du_writeback.c  --  R-Car Display Unit Writeback Support
+ *
+ * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_writeback.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+
+/**
+ * struct rcar_du_wb_conn_state - Driver-specific writeback connector state
+ * @state: base DRM connector state
+ * @format: format of the writeback framebuffer
+ */
+struct rcar_du_wb_conn_state {
+	struct drm_connector_state state;
+	const struct rcar_du_format_info *format;
+};
+
+
+#define to_rcar_wb_conn_state(s) \
+	container_of(s, struct rcar_du_wb_conn_state, state)
+
+/**
+ * struct rcar_du_wb_job - Driver-private data for writeback jobs
+ * @sg_tables: scatter-gather tables for the framebuffer memory
+ */
+struct rcar_du_wb_job {
+	struct sg_table sg_tables[3];
+};
+
+static int rcar_du_wb_conn_get_modes(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+
+	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
+				    dev->mode_config.max_height);
+}
+
+static int rcar_du_wb_prepare_job(struct drm_writeback_connector *connector,
+				  struct drm_writeback_job *job)
+{
+	struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
+	struct rcar_du_wb_job *rjob;
+	int ret;
+
+	if (!job->fb)
+		return 0;
+
+	rjob = kzalloc(sizeof(*rjob), GFP_KERNEL);
+	if (!rjob)
+		return -ENOMEM;
+
+	/* Map the framebuffer to the VSP. */
+	ret = rcar_du_vsp_map_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
+	if (ret < 0) {
+		kfree(rjob);
+		return ret;
+	}
+
+	job->priv = rjob;
+	return 0;
+}
+
+static void rcar_du_wb_cleanup_job(struct drm_writeback_connector *connector,
+				   struct drm_writeback_job *job)
+{
+	struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
+	struct rcar_du_wb_job *rjob = job->priv;
+
+	if (!job->fb)
+		return;
+
+	rcar_du_vsp_unmap_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
+	kfree(rjob);
+}
+
+static const struct drm_connector_helper_funcs rcar_du_wb_conn_helper_funcs = {
+	.get_modes = rcar_du_wb_conn_get_modes,
+	.prepare_writeback_job = rcar_du_wb_prepare_job,
+	.cleanup_writeback_job = rcar_du_wb_cleanup_job,
+};
+
+static const struct drm_connector_funcs rcar_du_wb_conn_funcs = {
+	.reset = drm_atomic_helper_connector_reset,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int rcar_du_wb_enc_atomic_check(struct drm_encoder *encoder,
+				       struct drm_crtc_state *crtc_state,
+				       struct drm_connector_state *conn_state)
+{
+	struct rcar_du_wb_conn_state *wb_state =
+		to_rcar_wb_conn_state(conn_state);
+	const struct drm_display_mode *mode = &crtc_state->mode;
+	struct drm_device *dev = encoder->dev;
+	struct drm_framebuffer *fb;
+
+	if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
+		return 0;
+
+	fb = conn_state->writeback_job->fb;
+
+	/*
+	 * Verify that the framebuffer format is supported and that its size
+	 * matches the current mode.
+	 */
+	if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) {
+		dev_dbg(dev->dev, "%s: invalid framebuffer size %ux%u\n",
+			__func__, fb->width, fb->height);
+		return -EINVAL;
+	}
+
+	wb_state->format = rcar_du_format_info(fb->format->format);
+	if (wb_state->format == NULL) {
+		dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__,
+			fb->format->format);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct drm_encoder_helper_funcs rcar_du_wb_enc_helper_funcs = {
+	.atomic_check = rcar_du_wb_enc_atomic_check,
+};
+
+/*
+ * Only RGB formats are currently supported as the VSP outputs RGB to the DU
+ * and can't convert to YUV separately for writeback.
+ */
+static const u32 writeback_formats[] = {
+	DRM_FORMAT_RGB332,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XRGB8888,
+};
+
+int rcar_du_writeback_init(struct rcar_du_device *rcdu,
+			   struct rcar_du_crtc *rcrtc)
+{
+	struct drm_writeback_connector *wb_conn = &rcrtc->writeback;
+
+	wb_conn->encoder.possible_crtcs = 1 << drm_crtc_index(&rcrtc->crtc);
+	drm_connector_helper_add(&wb_conn->base,
+				 &rcar_du_wb_conn_helper_funcs);
+
+	return drm_writeback_connector_init(rcdu->ddev, wb_conn,
+					    &rcar_du_wb_conn_funcs,
+					    &rcar_du_wb_enc_helper_funcs,
+					    writeback_formats,
+					    ARRAY_SIZE(writeback_formats));
+}
+
+void rcar_du_writeback_atomic_flush(struct rcar_du_crtc *rcrtc,
+				    struct vsp1_du_writeback_config *cfg)
+{
+	struct rcar_du_wb_conn_state *wb_state;
+	struct drm_connector_state *state;
+	struct rcar_du_wb_job *rjob;
+	struct drm_framebuffer *fb;
+	unsigned int i;
+
+	state = rcrtc->writeback.base.state;
+	if (!state || !state->writeback_job || !state->writeback_job->fb)
+		return;
+
+	fb = state->writeback_job->fb;
+	rjob = state->writeback_job->priv;
+	wb_state = to_rcar_wb_conn_state(state);
+
+	cfg->pixelformat = wb_state->format->v4l2;
+	cfg->pitch = fb->pitches[0];
+
+	for (i = 0; i < wb_state->format->planes; ++i)
+		cfg->mem[i] = sg_dma_address(rjob->sg_tables[i].sgl)
+			    + fb->offsets[i];
+
+	drm_writeback_queue_job(&rcrtc->writeback, state);
+}
+
+void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
+{
+	drm_writeback_signal_completion(&rcrtc->writeback, 0);
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_writeback.h b/drivers/gpu/drm/rcar-du/rcar_du_writeback.h
new file mode 100644
index 000000000000..d57c5a0bffe3
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_writeback.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * rcar_du_writeback.h  --  R-Car Display Unit Writeback Support
+ *
+ * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#ifndef __RCAR_DU_WRITEBACK_H__
+#define __RCAR_DU_WRITEBACK_H__
+
+#include <drm/drm_plane.h>
+
+struct rcar_du_crtc;
+struct rcar_du_device;
+struct vsp1_du_atomic_pipe_config;
+
+#ifdef CONFIG_DRM_RCAR_WRITEBACK
+int rcar_du_writeback_init(struct rcar_du_device *rcdu,
+			   struct rcar_du_crtc *rcrtc);
+void rcar_du_writeback_atomic_flush(struct rcar_du_crtc *rcrtc,
+				    struct vsp1_du_writeback_config *cfg);
+void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc);
+#else
+static inline int rcar_du_writeback_init(struct rcar_du_device *rcdu,
+					 struct rcar_du_crtc *rcrtc)
+{
+	return -ENXIO;
+}
+static inline void
+rcar_du_writeback_atomic_flush(struct rcar_du_crtc *rcrtc,
+			       struct vsp1_du_writeback_config *cfg)
+{
+}
+static inline void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
+{
+}
+#endif
+
+#endif /* __RCAR_DU_WRITEBACK_H__ */
-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions
  2019-02-21 10:32 ` [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions Laurent Pinchart
@ 2019-02-21 10:39   ` Laurent Pinchart
  2019-03-13  0:00     ` Laurent Pinchart
  2019-03-13  9:05     ` Kieran Bingham
  0 siblings, 2 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:39 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: James Qian Wang, Liviu Dudau, Kieran Bingham, dri-devel

Forgot to CC Rob, sorry about that.

On Thu, Feb 21, 2019 at 12:32:08PM +0200, Laurent Pinchart wrote:
> The msm_atomic_state_clear() and msm_atomic_state_free() functions are
> declared but never defined. Remove their prototypes.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/gpu/drm/msm/msm_drv.h | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 4e0c6c2f9a86..8f0287e75efb 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -240,8 +240,6 @@ int msm_atomic_prepare_fb(struct drm_plane *plane,
>  			  struct drm_plane_state *new_state);
>  void msm_atomic_commit_tail(struct drm_atomic_state *state);
>  struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
> -void msm_atomic_state_clear(struct drm_atomic_state *state);
> -void msm_atomic_state_free(struct drm_atomic_state *state);
>  
>  int msm_gem_init_vma(struct msm_gem_address_space *aspace,
>  		struct msm_gem_vma *vma, int npages);

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 10:32 ` [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job Laurent Pinchart
@ 2019-02-21 10:42   ` Laurent Pinchart
  2019-02-21 16:02     ` Brian Starkey
  2019-02-21 16:40   ` Eric Anholt
  2019-02-26 18:07   ` Liviu Dudau
  2 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 10:42 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: James Qian Wang, Liviu Dudau, Kieran Bingham, dri-devel

Forgot to CC Eric, sorry about that.

On Thu, Feb 21, 2019 at 12:32:05PM +0200, Laurent Pinchart wrote:
> The drm_writeback_queue_job() function takes ownership of the passed job
> and requires the caller to manually set the connector state
> writeback_job pointer to NULL. To simplify drivers and avoid errors
> (such as the missing NULL set in the vc4 driver), pass the connector
> state pointer to the function instead of the job pointer, and set the
> writeback_job pointer to NULL internally.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/gpu/drm/arm/malidp_mw.c |  3 +--
>  drivers/gpu/drm/drm_writeback.c | 15 ++++++++++-----
>  drivers/gpu/drm/vc4/vc4_txp.c   |  2 +-
>  include/drm/drm_writeback.h     |  2 +-
>  4 files changed, 13 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
> index 041a64dc7167..87627219ce3b 100644
> --- a/drivers/gpu/drm/arm/malidp_mw.c
> +++ b/drivers/gpu/drm/arm/malidp_mw.c
> @@ -252,8 +252,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm,
>  				     &mw_state->addrs[0],
>  				     mw_state->format);
>  
> -		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
> -		conn_state->writeback_job = NULL;
> +		drm_writeback_queue_job(mw_conn, conn_state);
>  		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
>  					   mw_state->pitches, mw_state->n_planes,
>  					   fb->width, fb->height, mw_state->format,
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index c20e6fe00cb3..338b993d7c9f 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -242,11 +242,12 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
>  /**
>   * drm_writeback_queue_job - Queue a writeback job for later signalling
>   * @wb_connector: The writeback connector to queue a job on
> - * @job: The job to queue
> + * @conn_state: The connector state containing the job to queue
>   *
> - * This function adds a job to the job_queue for a writeback connector. It
> - * should be considered to take ownership of the writeback job, and so any other
> - * references to the job must be cleared after calling this function.
> + * This function adds the job contained in @conn_state to the job_queue for a
> + * writeback connector. It takes ownership of the writeback job and sets the
> + * @conn_state->writeback_job to NULL, and so no access to the job may be
> + * performed by the caller after this function returns.
>   *
>   * Drivers must ensure that for a given writeback connector, jobs are queued in
>   * exactly the same order as they will be completed by the hardware (and
> @@ -258,10 +259,14 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
>   * See also: drm_writeback_signal_completion()
>   */
>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> -			     struct drm_writeback_job *job)
> +			     struct drm_connector_state *conn_state)
>  {
> +	struct drm_writeback_job *job;
>  	unsigned long flags;
>  
> +	job = conn_state->writeback_job;
> +	conn_state->writeback_job = NULL;
> +
>  	spin_lock_irqsave(&wb_connector->job_lock, flags);
>  	list_add_tail(&job->list_entry, &wb_connector->job_queue);
>  	spin_unlock_irqrestore(&wb_connector->job_lock, flags);
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index aa279b5b0de7..5dabd91f2d7e 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -327,7 +327,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
>  
>  	TXP_WRITE(TXP_DST_CTRL, ctrl);
>  
> -	drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
> +	drm_writeback_queue_job(&txp->connector, conn_state);
>  }
>  
>  static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> index 23df9d463003..47662c362743 100644
> --- a/include/drm/drm_writeback.h
> +++ b/include/drm/drm_writeback.h
> @@ -123,7 +123,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
>  				 const u32 *formats, int n_formats);
>  
>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> -			     struct drm_writeback_job *job);
> +			     struct drm_connector_state *conn_state);
>  
>  void drm_writeback_cleanup_job(struct drm_writeback_job *job);
>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously"
  2019-02-21 10:31 ` [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously" Laurent Pinchart
@ 2019-02-21 13:16   ` Kieran Bingham
  0 siblings, 0 replies; 65+ messages in thread
From: Kieran Bingham @ 2019-02-21 13:16 UTC (permalink / raw)
  To: Laurent Pinchart, dri-devel; +Cc: Liviu Dudau, James Qian Wang

Hi Laurent,

On 21/02/2019 10:31, Laurent Pinchart wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> This reverts commit 3299ba5c0b21 ("[media] v4l: vsp1: Supply frames to
> the DU continuously")
> 
> The DU output mode does not rely on frames being supplied on the WPF as
> its pipeline is supplied from DRM. For the upcoming WPF writeback
> functionality, we will choose to enable writeback mode if there is an
> output buffer, or disable it (leaving the existing display pipeline
> unharmed) otherwise.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

Don't forget to add your SoB or such here :)
--
Regards

Kieran



> ---
>  drivers/media/platform/vsp1/vsp1_video.c | 11 -----------
>  1 file changed, 11 deletions(-)
> 
> diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
> index 7ceaf3222145..328d686189be 100644
> --- a/drivers/media/platform/vsp1/vsp1_video.c
> +++ b/drivers/media/platform/vsp1/vsp1_video.c
> @@ -307,11 +307,6 @@ static int 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 *
> @@ -333,12 +328,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))
> 

-- 
Regards
--
Kieran
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-02-21 10:32 ` [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list Laurent Pinchart
@ 2019-02-21 13:16   ` Kieran Bingham
  2019-02-22 14:30   ` Brian Starkey
  1 sibling, 0 replies; 65+ messages in thread
From: Kieran Bingham @ 2019-02-21 13:16 UTC (permalink / raw)
  To: Laurent Pinchart, dri-devel; +Cc: Liviu Dudau, James Qian Wang

Hi Laurent,

On 21/02/2019 10:32, Laurent Pinchart wrote:
> One-shot entries are used as an alternative to committing a complete new
> display list when a couple of registers need to be written for one frame
> and then reset to another value for all subsequent frames. This will be
> used to implement writeback support that will need to enable writeback
> for the duration of a single frame.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Thanks for adding the documentation, and the new name _oneshot() sounds
fine to me.


Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

> ---
>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
>  2 files changed, 81 insertions(+)
> 
> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> index 886b3a69d329..7b4d252bfde7 100644
> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
>  
>  	unsigned int num_entries;
>  	unsigned int max_entries;
> +
> +	unsigned int num_patches;
> +	struct {
> +		struct vsp1_dl_entry *entry;
> +		u32 data;
> +	} patches[2];
>  };
>  
>  /**
> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
>  		return;
>  
>  	dlb->num_entries = 0;
> +	dlb->num_patches = 0;
>  
>  	spin_lock_irqsave(&dlb->pool->lock, flags);
>  	list_add_tail(&dlb->free, &dlb->pool->free);
> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
>  	dlb->num_entries++;
>  }
>  
> +/**
> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> + *	single frame
> + * @dlb: The body
> + * @reg: The register address
> + * @value: The register value
> + * @reset_value: The value to reset the register to at the next vblank
> + *
> + * Display lists in continuous mode are re-used by the hardware for successive
> + * frames until a new display list is committed. Changing the VSP configuration
> + * normally requires creating and committing a new display list. This function
> + * offers an alternative race-free way by writing a @value to the @register in
> + * the display list body for a single frame, specifying in @reset_value the
> + * value to reset the register to one vblank after the display list is
> + * committed.
> + *
> + * The maximum number of one-shot entries is limited to 2 per display list body,
> + * and one-shot entries are counted in the total number of entries specified
> + * when the body is allocated by vsp1_dl_body_alloc().
> + */
> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> +				u32 reset_value)
> +{
> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> +		return;
> +
> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> +		      "DLB patches size exceeded (max %zu)",
> +		      ARRAY_SIZE(dlb->patches)))
> +		return;
> +
> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> +	dlb->patches[dlb->num_patches].data = reset_value;
> +	dlb->num_patches++;
> +
> +	dlb->entries[dlb->num_entries].addr = reg;
> +	dlb->entries[dlb->num_entries].data = value;
> +	dlb->num_entries++;
> +}
> +
>  /* -----------------------------------------------------------------------------
>   * Display List Extended Command Management
>   */
> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
>  	 * has at least one body, thus we reinitialise the entries list.
>  	 */
>  	dl->body0->num_entries = 0;
> +	dl->body0->num_patches = 0;
>  
>  	list_add_tail(&dl->list, &dl->dlm->free);
>  }
> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
>   * Display List Manager
>   */
>  
> +/**
> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> + *	interrupt
> + * @dlm: the display list manager
> + *
> + * Apply all one-shot patches registered for the active display list.
> + */
> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> +{
> +	struct vsp1_dl_body *dlb;
> +	struct vsp1_dl_list *dl;
> +	unsigned int i;
> +
> +	spin_lock(&dlm->lock);
> +
> +	dl = dlm->active;
> +	if (!dl)
> +		goto done;
> +
> +	list_for_each_entry(dlb, &dl->bodies, list) {
> +		for (i = 0; i < dlb->num_patches; ++i)
> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> +		dlb->num_patches = 0;
> +	}
> +
> +done:
> +	spin_unlock(&dlm->lock);
> +}
> +
>  /**
>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
>   * @dlm: the display list manager
> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> index e0fdb145e6ed..f845607abc4c 100644
> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
>  					unsigned int prealloc);
>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
>  
> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
>  
>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> +				u32 reset_value);
>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
>  
> 

-- 
Regards
--
Kieran
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 10:42   ` Laurent Pinchart
@ 2019-02-21 16:02     ` Brian Starkey
  2019-02-21 21:56       ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-02-21 16:02 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Thu, Feb 21, 2019 at 12:42:25PM +0200, Laurent Pinchart wrote:
> Forgot to CC Eric, sorry about that.
> 
> On Thu, Feb 21, 2019 at 12:32:05PM +0200, Laurent Pinchart wrote:
> > The drm_writeback_queue_job() function takes ownership of the passed job
> > and requires the caller to manually set the connector state
> > writeback_job pointer to NULL. To simplify drivers and avoid errors
> > (such as the missing NULL set in the vc4 driver), pass the connector
> > state pointer to the function instead of the job pointer, and set the
> > writeback_job pointer to NULL internally.

LGTM, it was a mistake not doing it like this from the start.

I do have one suggestion below, but either way:

Reviewed-by: Brian Starkey <brian.starkey@arm.com>

> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> >  drivers/gpu/drm/arm/malidp_mw.c |  3 +--
> >  drivers/gpu/drm/drm_writeback.c | 15 ++++++++++-----
> >  drivers/gpu/drm/vc4/vc4_txp.c   |  2 +-
> >  include/drm/drm_writeback.h     |  2 +-
> >  4 files changed, 13 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
> > index 041a64dc7167..87627219ce3b 100644
> > --- a/drivers/gpu/drm/arm/malidp_mw.c
> > +++ b/drivers/gpu/drm/arm/malidp_mw.c
> > @@ -252,8 +252,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm,
> >  				     &mw_state->addrs[0],
> >  				     mw_state->format);
> >  
> > -		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
> > -		conn_state->writeback_job = NULL;
> > +		drm_writeback_queue_job(mw_conn, conn_state);
> >  		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
> >  					   mw_state->pitches, mw_state->n_planes,
> >  					   fb->width, fb->height, mw_state->format,
> > diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> > index c20e6fe00cb3..338b993d7c9f 100644
> > --- a/drivers/gpu/drm/drm_writeback.c
> > +++ b/drivers/gpu/drm/drm_writeback.c
> > @@ -242,11 +242,12 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
> >  /**
> >   * drm_writeback_queue_job - Queue a writeback job for later signalling
> >   * @wb_connector: The writeback connector to queue a job on
> > - * @job: The job to queue
> > + * @conn_state: The connector state containing the job to queue
> >   *
> > - * This function adds a job to the job_queue for a writeback connector. It
> > - * should be considered to take ownership of the writeback job, and so any other
> > - * references to the job must be cleared after calling this function.
> > + * This function adds the job contained in @conn_state to the job_queue for a
> > + * writeback connector. It takes ownership of the writeback job and sets the
> > + * @conn_state->writeback_job to NULL, and so no access to the job may be
> > + * performed by the caller after this function returns.
> >   *
> >   * Drivers must ensure that for a given writeback connector, jobs are queued in
> >   * exactly the same order as they will be completed by the hardware (and
> > @@ -258,10 +259,14 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
> >   * See also: drm_writeback_signal_completion()
> >   */
> >  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> > -			     struct drm_writeback_job *job)
> > +			     struct drm_connector_state *conn_state)
> >  {
> > +	struct drm_writeback_job *job;
> >  	unsigned long flags;
> >  
> > +	job = conn_state->writeback_job;

What do you think about adding a defensive WARN_ON(!job)?

> > +	conn_state->writeback_job = NULL;
> > +
> >  	spin_lock_irqsave(&wb_connector->job_lock, flags);
> >  	list_add_tail(&job->list_entry, &wb_connector->job_queue);
> >  	spin_unlock_irqrestore(&wb_connector->job_lock, flags);
> > diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> > index aa279b5b0de7..5dabd91f2d7e 100644
> > --- a/drivers/gpu/drm/vc4/vc4_txp.c
> > +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> > @@ -327,7 +327,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
> >  
> >  	TXP_WRITE(TXP_DST_CTRL, ctrl);
> >  
> > -	drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
> > +	drm_writeback_queue_job(&txp->connector, conn_state);
> >  }
> >  
> >  static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
> > diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> > index 23df9d463003..47662c362743 100644
> > --- a/include/drm/drm_writeback.h
> > +++ b/include/drm/drm_writeback.h
> > @@ -123,7 +123,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >  				 const u32 *formats, int n_formats);
> >  
> >  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> > -			     struct drm_writeback_job *job);
> > +			     struct drm_connector_state *conn_state);
> >  
> >  void drm_writeback_cleanup_job(struct drm_writeback_job *job);
> >  
> 
> -- 
> Regards,
> 
> Laurent Pinchart
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 10:32 ` [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job Laurent Pinchart
  2019-02-21 10:42   ` Laurent Pinchart
@ 2019-02-21 16:40   ` Eric Anholt
  2019-02-26 18:07   ` Liviu Dudau
  2 siblings, 0 replies; 65+ messages in thread
From: Eric Anholt @ 2019-02-21 16:40 UTC (permalink / raw)
  To: Laurent Pinchart, dri-devel; +Cc: James Qian Wang, Liviu Dudau, Kieran Bingham


[-- Attachment #1.1: Type: text/plain, Size: 585 bytes --]

Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> writes:

> The drm_writeback_queue_job() function takes ownership of the passed job
> and requires the caller to manually set the connector state
> writeback_job pointer to NULL. To simplify drivers and avoid errors
> (such as the missing NULL set in the vc4 driver), pass the connector
> state pointer to the function instead of the job pointer, and set the
> writeback_job pointer to NULL internally.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Acked-by: Eric Anholt <eric@anholt.net>

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 13/19] drm: writeback: Fix leak of writeback job
  2019-02-21 10:32 ` [PATCH v5 13/19] drm: writeback: Fix leak of writeback job Laurent Pinchart
@ 2019-02-21 17:48   ` Brian Starkey
  2019-02-26 18:10   ` Liviu Dudau
  1 sibling, 0 replies; 65+ messages in thread
From: Brian Starkey @ 2019-02-21 17:48 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Liviu Dudau, nd, james qian wang (Arm Technology China),
	Kieran Bingham, dri-devel

Hi,

On Thu, Feb 21, 2019 at 12:32:06PM +0200, Laurent Pinchart wrote:
> Writeback jobs are allocated when the WRITEBACK_FB_ID is set, and
> deleted when the jobs complete. This results in both a memory leak of
> the job and a leak of the framebuffer if the atomic commit returns
> before the job is queued for processing, for instance if the atomic
> check fails or if the commit runs in test-only mode.
> 
> Fix this by implementing the drm_writeback_cleanup_job() function and
> calling it from __drm_atomic_helper_connector_destroy_state(). As
> writeback jobs are removed from the state when they're queued for
> processing, any job left in the state when the state gets destroyed
> needs to be cleaned up.
> 
> The existing declaration of the drm_writeback_cleanup_job() function
> without an implementation hints that this problem was considered, but
> never addressed.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Thanks for fixing this, it looks like it got dropped in one of the
rework/rebases.

Reviewed-by: Brian Starkey <brian.starkey@arm.com>

> ---
>  drivers/gpu/drm/drm_atomic_state_helper.c |  4 ++++
>  drivers/gpu/drm/drm_writeback.c           | 13 ++++++++++---
>  2 files changed, 14 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 4985384e51f6..59ffb6b9c745 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -30,6 +30,7 @@
>  #include <drm/drm_connector.h>
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_device.h>
> +#include <drm/drm_writeback.h>
>  
>  #include <linux/slab.h>
>  #include <linux/dma-fence.h>
> @@ -412,6 +413,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
>  
>  	if (state->commit)
>  		drm_crtc_commit_put(state->commit);
> +
> +	if (state->writeback_job)
> +		drm_writeback_cleanup_job(state->writeback_job);
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
>  
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index 338b993d7c9f..afb1ae6e0ecb 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -273,6 +273,14 @@ void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
>  }
>  EXPORT_SYMBOL(drm_writeback_queue_job);
>  
> +void drm_writeback_cleanup_job(struct drm_writeback_job *job)
> +{
> +	if (job->fb)
> +		drm_framebuffer_put(job->fb);
> +
> +	kfree(job);
> +}
> +
>  /*
>   * @cleanup_work: deferred cleanup of a writeback job
>   *
> @@ -285,10 +293,9 @@ static void cleanup_work(struct work_struct *work)
>  	struct drm_writeback_job *job = container_of(work,
>  						     struct drm_writeback_job,
>  						     cleanup_work);
> -	drm_framebuffer_put(job->fb);
> -	kfree(job);
> -}
>  
> +	drm_writeback_cleanup_job(job);
> +}
>  
>  /**
>   * drm_writeback_signal_completion - Signal the completion of a writeback job
> -- 
> Regards,
> 
> Laurent Pinchart
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-21 10:32 ` [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations Laurent Pinchart
@ 2019-02-21 18:12   ` Brian Starkey
  2019-02-21 22:12     ` Laurent Pinchart
  2019-02-26 18:39   ` Liviu Dudau
  1 sibling, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-02-21 18:12 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Liviu Dudau, nd, james qian wang (Arm Technology China),
	Kieran Bingham, dri-devel

Hi Laurent,

On Thu, Feb 21, 2019 at 12:32:07PM +0200, Laurent Pinchart wrote:
> As writeback jobs contain a framebuffer, drivers may need to prepare and
> cleanup them the same way they can prepare and cleanup framebuffers for
> planes. Add two new optional connector helper operations,
> .prepare_writeback_job() and .cleanup_writeback_job() to support this.
> 
> The job prepare operation is called from
> drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
> that would need to be called by all drivers not using
> drm_atomic_helper_commit(). The job cleanup operation is called from the
> existing drm_writeback_cleanup_job() function, invoked both when
> destroying the job as part of a aborted commit, or when the job
> completes.
> 
> The drm_writeback_job structure is extended with a priv field to let
> drivers store per-job data, such as mappings related to the writeback
> framebuffer.
> 
> For internal plumbing reasons the drm_writeback_job structure needs to
> store a back-pointer to the drm_writeback_connector. To avoid pushing
> too much writeback-specific knowledge to drm_atomic_uapi.c, create a
> drm_writeback_set_fb() function, move the writeback job setup code
> there, and set the connector backpointer. The prepare_signaling()
> function doesn't need to allocate writeback jobs and can ignore
> connectors without a job, as it is called after the writeback jobs are
> allocated to store framebuffers, and a writeback fence with a
> framebuffer is an invalid configuration that gets rejected by the commit
> check.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

I probably need to revisit this with fresh eyes tomorrow, but how come
the prepared-ness and whatever is being prepared can't be put into a
subclassed framebuffer? That seems more natural to me, than tracking
it with the job. It's really the framebuffer we're preparing after
all.

I guess if you have the same framebuffer in use for multiple things,
you might need to track it separately? Can the mappings be shared in
that case?

> ---
>  drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
>  drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
>  drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
>  include/drm/drm_modeset_helper_vtables.h |  7 ++++
>  include/drm/drm_writeback.h              | 28 ++++++++++++++-
>  5 files changed, 96 insertions(+), 24 deletions(-)
> 
> This patch is currently missing documentation for the
> .prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
> to fix this, but first wanted feedback on the direction taken. I'm not
> entirely happy with the priv pointer in the drm_writeback_job structure,
> but adding a full state duplicate/destroy machinery for that structure
> was equally unappealing to me.
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 6fe2303fccd9..70a4886c6e65 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
>  int drm_atomic_helper_prepare_planes(struct drm_device *dev,
>  				     struct drm_atomic_state *state)
>  {
> +	struct drm_connector *connector;
> +	struct drm_connector_state *new_conn_state;
>  	struct drm_plane *plane;
>  	struct drm_plane_state *new_plane_state;
>  	int ret, i, j;
>  
> +	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
> +		if (!new_conn_state->writeback_job)
> +			continue;
> +
> +		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
>  	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
>  		const struct drm_plane_helper_funcs *funcs;
>  
> diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
> index c40889888a16..e802152a01ad 100644
> --- a/drivers/gpu/drm/drm_atomic_uapi.c
> +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> @@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
>  	return 0;
>  }
>  
> -static struct drm_writeback_job *
> -drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
> -{
> -	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> -
> -	if (!conn_state->writeback_job)
> -		conn_state->writeback_job =
> -			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> -
> -	return conn_state->writeback_job;
> -}
> -
>  static int drm_atomic_set_writeback_fb_for_connector(
>  		struct drm_connector_state *conn_state,
>  		struct drm_framebuffer *fb)
>  {
> -	struct drm_writeback_job *job =
> -		drm_atomic_get_writeback_job(conn_state);
> -	if (!job)
> -		return -ENOMEM;
> +	int ret;
>  
> -	drm_framebuffer_assign(&job->fb, fb);
> +	ret = drm_writeback_set_fb(conn_state, fb);
> +	if (ret < 0)
> +		return ret;
>  
>  	if (fb)
>  		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
> @@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
>  
>  	for_each_new_connector_in_state(state, conn, conn_state, i) {
>  		struct drm_writeback_connector *wb_conn;
> -		struct drm_writeback_job *job;
>  		struct drm_out_fence_state *f;
>  		struct dma_fence *fence;
>  		s32 __user *fence_ptr;
>  
> +		if (!conn_state->writeback_job)
> +			continue;
> +
>  		fence_ptr = get_out_fence_for_connector(state, conn);
>  		if (!fence_ptr)
>  			continue;
>  
> -		job = drm_atomic_get_writeback_job(conn_state);
> -		if (!job)
> -			return -ENOMEM;
> -
>  		f = krealloc(*fence_state, sizeof(**fence_state) *
>  			     (*num_fences + 1), GFP_KERNEL);
>  		if (!f)
> @@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
>  			return ret;
>  		}
>  
> -		job->out_fence = fence;
> +		conn_state->writeback_job->out_fence = fence;
>  	}
>  
>  	/*
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index afb1ae6e0ecb..4678d61d634a 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
>  }
>  EXPORT_SYMBOL(drm_writeback_connector_init);
>  
> +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> +			 struct drm_framebuffer *fb)
> +{
> +	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> +
> +	if (!conn_state->writeback_job) {
> +		conn_state->writeback_job =
> +			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> +		if (!conn_state->writeback_job)
> +			return -ENOMEM;
> +
> +		conn_state->writeback_job->connector =
> +			drm_connector_to_writeback(conn_state->connector);
> +	}
> +
> +	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
> +	return 0;
> +}
> +
> +int drm_writeback_prepare_job(struct drm_writeback_job *job)
> +{
> +	struct drm_writeback_connector *connector = job->connector;
> +	const struct drm_connector_helper_funcs *funcs =
> +		connector->base.helper_private;
> +	int ret;
> +
> +	if (funcs->cleanup_writeback_job) {
> +		ret = funcs->prepare_writeback_job(connector, job);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	job->prepared = true;
> +	return 0;
> +}
> +
>  /**
>   * drm_writeback_queue_job - Queue a writeback job for later signalling
>   * @wb_connector: The writeback connector to queue a job on
> @@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
>  
>  void drm_writeback_cleanup_job(struct drm_writeback_job *job)
>  {
> +	struct drm_writeback_connector *connector = job->connector;
> +	const struct drm_connector_helper_funcs *funcs =
> +		connector->base.helper_private;
> +
> +	if (job->prepared && funcs->cleanup_writeback_job)
> +		funcs->cleanup_writeback_job(connector, job);
> +
>  	if (job->fb)
>  		drm_framebuffer_put(job->fb);
>  
> diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> index 61142aa0ab23..73d03fe66799 100644
> --- a/include/drm/drm_modeset_helper_vtables.h
> +++ b/include/drm/drm_modeset_helper_vtables.h
> @@ -49,6 +49,8 @@
>   */
>  
>  enum mode_set_atomic;
> +struct drm_writeback_connector;
> +struct drm_writeback_job;
>  
>  /**
>   * struct drm_crtc_helper_funcs - helper operations for CRTCs
> @@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
>  	 */
>  	void (*atomic_commit)(struct drm_connector *connector,
>  			      struct drm_connector_state *state);
> +
> +	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
> +				     struct drm_writeback_job *job);
> +	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
> +				      struct drm_writeback_job *job);
>  };
>  
>  /**
> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> index 47662c362743..777c14c847f0 100644
> --- a/include/drm/drm_writeback.h
> +++ b/include/drm/drm_writeback.h
> @@ -79,6 +79,20 @@ struct drm_writeback_connector {
>  };
>  
>  struct drm_writeback_job {
> +	/**
> +	 * @connector:
> +	 *
> +	 * Back-pointer to the writeback connector associated with the job
> +	 */
> +	struct drm_writeback_connector *connector;

It kind-of feels like this shouldn't be necessary, but I think
avoiding it would mean either allocating the work_struct outside of
the job, and tracking the connector there instead of in the job, or
having two calls to unprepare - one in signal_completion and one in
destroy_state.

It's probably not worth the gymnastics to avoid the backpointer... but
for some reason the backpointer feels intangibly dirty to me.

Thanks,
-Brian

> +
> +	/**
> +	 * @prepared:
> +	 *
> +	 * Set when the job has been prepared with drm_writeback_prepare_job()
> +	 */
> +	bool prepared;
> +
>  	/**
>  	 * @cleanup_work:
>  	 *
> @@ -98,7 +112,7 @@ struct drm_writeback_job {
>  	 * @fb:
>  	 *
>  	 * Framebuffer to be written to by the writeback connector. Do not set
> -	 * directly, use drm_atomic_set_writeback_fb_for_connector()
> +	 * directly, use drm_writeback_set_fb()
>  	 */
>  	struct drm_framebuffer *fb;
>  
> @@ -108,6 +122,13 @@ struct drm_writeback_job {
>  	 * Fence which will signal once the writeback has completed
>  	 */
>  	struct dma_fence *out_fence;
> +
> +	/**
> +	 * @priv:
> +	 *
> +	 * Driver-private data
> +	 */
> +	void *priv;
>  };
>  
>  static inline struct drm_writeback_connector *
> @@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
>  				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
>  				 const u32 *formats, int n_formats);
>  
> +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> +			 struct drm_framebuffer *fb);
> +
> +int drm_writeback_prepare_job(struct drm_writeback_job *job);
> +
>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
>  			     struct drm_connector_state *conn_state);
>  
> -- 
> Regards,
> 
> Laurent Pinchart
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 16:02     ` Brian Starkey
@ 2019-02-21 21:56       ` Laurent Pinchart
  2019-02-22 13:33         ` Brian Starkey
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 21:56 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Thu, Feb 21, 2019 at 04:02:37PM +0000, Brian Starkey wrote:
> On Thu, Feb 21, 2019 at 12:42:25PM +0200, Laurent Pinchart wrote:
> > On Thu, Feb 21, 2019 at 12:32:05PM +0200, Laurent Pinchart wrote:
> >> The drm_writeback_queue_job() function takes ownership of the passed job
> >> and requires the caller to manually set the connector state
> >> writeback_job pointer to NULL. To simplify drivers and avoid errors
> >> (such as the missing NULL set in the vc4 driver), pass the connector
> >> state pointer to the function instead of the job pointer, and set the
> >> writeback_job pointer to NULL internally.
> 
> LGTM, it was a mistake not doing it like this from the start.
> 
> I do have one suggestion below, but either way:
> 
> Reviewed-by: Brian Starkey <brian.starkey@arm.com>
> 
> >> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >> ---
> >>  drivers/gpu/drm/arm/malidp_mw.c |  3 +--
> >>  drivers/gpu/drm/drm_writeback.c | 15 ++++++++++-----
> >>  drivers/gpu/drm/vc4/vc4_txp.c   |  2 +-
> >>  include/drm/drm_writeback.h     |  2 +-
> >>  4 files changed, 13 insertions(+), 9 deletions(-)
> >> 
> >> diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
> >> index 041a64dc7167..87627219ce3b 100644
> >> --- a/drivers/gpu/drm/arm/malidp_mw.c
> >> +++ b/drivers/gpu/drm/arm/malidp_mw.c
> >> @@ -252,8 +252,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm,
> >>  				     &mw_state->addrs[0],
> >>  				     mw_state->format);
> >>  
> >> -		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
> >> -		conn_state->writeback_job = NULL;
> >> +		drm_writeback_queue_job(mw_conn, conn_state);
> >>  		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
> >>  					   mw_state->pitches, mw_state->n_planes,
> >>  					   fb->width, fb->height, mw_state->format,
> >> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> >> index c20e6fe00cb3..338b993d7c9f 100644
> >> --- a/drivers/gpu/drm/drm_writeback.c
> >> +++ b/drivers/gpu/drm/drm_writeback.c
> >> @@ -242,11 +242,12 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
> >>  /**
> >>   * drm_writeback_queue_job - Queue a writeback job for later signalling
> >>   * @wb_connector: The writeback connector to queue a job on
> >> - * @job: The job to queue
> >> + * @conn_state: The connector state containing the job to queue
> >>   *
> >> - * This function adds a job to the job_queue for a writeback connector. It
> >> - * should be considered to take ownership of the writeback job, and so any other
> >> - * references to the job must be cleared after calling this function.
> >> + * This function adds the job contained in @conn_state to the job_queue for a
> >> + * writeback connector. It takes ownership of the writeback job and sets the
> >> + * @conn_state->writeback_job to NULL, and so no access to the job may be
> >> + * performed by the caller after this function returns.
> >>   *
> >>   * Drivers must ensure that for a given writeback connector, jobs are queued in
> >>   * exactly the same order as they will be completed by the hardware (and
> >> @@ -258,10 +259,14 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
> >>   * See also: drm_writeback_signal_completion()
> >>   */
> >>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> >> -			     struct drm_writeback_job *job)
> >> +			     struct drm_connector_state *conn_state)
> >>  {
> >> +	struct drm_writeback_job *job;
> >>  	unsigned long flags;
> >>  
> >> +	job = conn_state->writeback_job;
> 
> What do you think about adding a defensive WARN_ON(!job)?

I expect this function to be called from an atomic commit handler for
the writeback job, which will need to access the job's contents (in
particular the framebuffer). I thus think we'll never be called with a
NULL job here, so a WARN_ON would only consume a bit of CPU time and
memory without adding any safeguard. If your analysis differs and you
still think there's a risk, I'll add it.

> >> +	conn_state->writeback_job = NULL;
> >> +
> >>  	spin_lock_irqsave(&wb_connector->job_lock, flags);
> >>  	list_add_tail(&job->list_entry, &wb_connector->job_queue);
> >>  	spin_unlock_irqrestore(&wb_connector->job_lock, flags);
> >> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> >> index aa279b5b0de7..5dabd91f2d7e 100644
> >> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> >> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> >> @@ -327,7 +327,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
> >>  
> >>  	TXP_WRITE(TXP_DST_CTRL, ctrl);
> >>  
> >> -	drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
> >> +	drm_writeback_queue_job(&txp->connector, conn_state);
> >>  }
> >>  
> >>  static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
> >> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> >> index 23df9d463003..47662c362743 100644
> >> --- a/include/drm/drm_writeback.h
> >> +++ b/include/drm/drm_writeback.h
> >> @@ -123,7 +123,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >>  				 const u32 *formats, int n_formats);
> >>  
> >>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> >> -			     struct drm_writeback_job *job);
> >> +			     struct drm_connector_state *conn_state);
> >>  
> >>  void drm_writeback_cleanup_job(struct drm_writeback_job *job);
> >>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-21 18:12   ` Brian Starkey
@ 2019-02-21 22:12     ` Laurent Pinchart
  2019-02-22 13:50       ` Brian Starkey
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-21 22:12 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Thu, Feb 21, 2019 at 06:12:03PM +0000, Brian Starkey wrote:
> On Thu, Feb 21, 2019 at 12:32:07PM +0200, Laurent Pinchart wrote:
> > As writeback jobs contain a framebuffer, drivers may need to prepare and
> > cleanup them the same way they can prepare and cleanup framebuffers for
> > planes. Add two new optional connector helper operations,
> > .prepare_writeback_job() and .cleanup_writeback_job() to support this.
> > 
> > The job prepare operation is called from
> > drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
> > that would need to be called by all drivers not using
> > drm_atomic_helper_commit(). The job cleanup operation is called from the
> > existing drm_writeback_cleanup_job() function, invoked both when
> > destroying the job as part of a aborted commit, or when the job
> > completes.
> > 
> > The drm_writeback_job structure is extended with a priv field to let
> > drivers store per-job data, such as mappings related to the writeback
> > framebuffer.
> > 
> > For internal plumbing reasons the drm_writeback_job structure needs to
> > store a back-pointer to the drm_writeback_connector. To avoid pushing
> > too much writeback-specific knowledge to drm_atomic_uapi.c, create a
> > drm_writeback_set_fb() function, move the writeback job setup code
> > there, and set the connector backpointer. The prepare_signaling()
> > function doesn't need to allocate writeback jobs and can ignore
> > connectors without a job, as it is called after the writeback jobs are
> > allocated to store framebuffers, and a writeback fence with a
> > framebuffer is an invalid configuration that gets rejected by the commit
> > check.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> 
> I probably need to revisit this with fresh eyes tomorrow, but how come
> the prepared-ness and whatever is being prepared can't be put into a
> subclassed framebuffer? That seems more natural to me, than tracking
> it with the job. It's really the framebuffer we're preparing after
> all.

In my patch series I indeed need to track information related to the
framebuffer only, but is it a good idea to limit writeback
prepare/cleanup to that ? Other drivers may have different needs.

Furthermore, the mapping needs to exist for the lifetime of the
writeback job, so I'd have to add a counter to the framebuffer subclass
to track these too. While doable, it starts sounding a bit like a hack,
and may create race conditions.

> I guess if you have the same framebuffer in use for multiple things,
> you might need to track it separately? Can the mappings be shared in
> that case?

Speaking about my case, the mapping is per-CRTC, so if I use the same
framebuffer for multiple CRTCs I'd need separate mappings. Other drivers
may have simpler or more complex needs.

When you'll revisit this with fresh eyes, I'd like to know if you think
it would be worth it replacing the priv pointer with allocate/destroy
operations to allow subclassing the writeback job. We could possibly do
without the destroy operation if we mandate embedding the writeback job
as the first field of the subclass and usage of kmalloc (& friends) to
allocate the subclass structure. We could even do without the allocate
operation if we just stored the size of the subclass in the writeback
connector itself, but that would depart from what we usually do in DRM.

I would also like to know if I should create a writeback connector
operations structure to store the prepare/cleanup and perhaps
allocate/destroy operations instead of adding them to the connector
helper funcs.

> > ---
> >  drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
> >  drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
> >  drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
> >  include/drm/drm_modeset_helper_vtables.h |  7 ++++
> >  include/drm/drm_writeback.h              | 28 ++++++++++++++-
> >  5 files changed, 96 insertions(+), 24 deletions(-)
> > 
> > This patch is currently missing documentation for the
> > .prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
> > to fix this, but first wanted feedback on the direction taken. I'm not
> > entirely happy with the priv pointer in the drm_writeback_job structure,
> > but adding a full state duplicate/destroy machinery for that structure
> > was equally unappealing to me.
> > 
> > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> > index 6fe2303fccd9..70a4886c6e65 100644
> > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > @@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
> >  int drm_atomic_helper_prepare_planes(struct drm_device *dev,
> >  				     struct drm_atomic_state *state)
> >  {
> > +	struct drm_connector *connector;
> > +	struct drm_connector_state *new_conn_state;
> >  	struct drm_plane *plane;
> >  	struct drm_plane_state *new_plane_state;
> >  	int ret, i, j;
> >  
> > +	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
> > +		if (!new_conn_state->writeback_job)
> > +			continue;
> > +
> > +		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> >  	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
> >  		const struct drm_plane_helper_funcs *funcs;
> >  
> > diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
> > index c40889888a16..e802152a01ad 100644
> > --- a/drivers/gpu/drm/drm_atomic_uapi.c
> > +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> > @@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> >  	return 0;
> >  }
> >  
> > -static struct drm_writeback_job *
> > -drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
> > -{
> > -	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> > -
> > -	if (!conn_state->writeback_job)
> > -		conn_state->writeback_job =
> > -			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> > -
> > -	return conn_state->writeback_job;
> > -}
> > -
> >  static int drm_atomic_set_writeback_fb_for_connector(
> >  		struct drm_connector_state *conn_state,
> >  		struct drm_framebuffer *fb)
> >  {
> > -	struct drm_writeback_job *job =
> > -		drm_atomic_get_writeback_job(conn_state);
> > -	if (!job)
> > -		return -ENOMEM;
> > +	int ret;
> >  
> > -	drm_framebuffer_assign(&job->fb, fb);
> > +	ret = drm_writeback_set_fb(conn_state, fb);
> > +	if (ret < 0)
> > +		return ret;
> >  
> >  	if (fb)
> >  		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
> > @@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
> >  
> >  	for_each_new_connector_in_state(state, conn, conn_state, i) {
> >  		struct drm_writeback_connector *wb_conn;
> > -		struct drm_writeback_job *job;
> >  		struct drm_out_fence_state *f;
> >  		struct dma_fence *fence;
> >  		s32 __user *fence_ptr;
> >  
> > +		if (!conn_state->writeback_job)
> > +			continue;
> > +
> >  		fence_ptr = get_out_fence_for_connector(state, conn);
> >  		if (!fence_ptr)
> >  			continue;
> >  
> > -		job = drm_atomic_get_writeback_job(conn_state);
> > -		if (!job)
> > -			return -ENOMEM;
> > -
> >  		f = krealloc(*fence_state, sizeof(**fence_state) *
> >  			     (*num_fences + 1), GFP_KERNEL);
> >  		if (!f)
> > @@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
> >  			return ret;
> >  		}
> >  
> > -		job->out_fence = fence;
> > +		conn_state->writeback_job->out_fence = fence;
> >  	}
> >  
> >  	/*
> > diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> > index afb1ae6e0ecb..4678d61d634a 100644
> > --- a/drivers/gpu/drm/drm_writeback.c
> > +++ b/drivers/gpu/drm/drm_writeback.c
> > @@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >  }
> >  EXPORT_SYMBOL(drm_writeback_connector_init);
> >  
> > +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> > +			 struct drm_framebuffer *fb)
> > +{
> > +	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> > +
> > +	if (!conn_state->writeback_job) {
> > +		conn_state->writeback_job =
> > +			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> > +		if (!conn_state->writeback_job)
> > +			return -ENOMEM;
> > +
> > +		conn_state->writeback_job->connector =
> > +			drm_connector_to_writeback(conn_state->connector);
> > +	}
> > +
> > +	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
> > +	return 0;
> > +}
> > +
> > +int drm_writeback_prepare_job(struct drm_writeback_job *job)
> > +{
> > +	struct drm_writeback_connector *connector = job->connector;
> > +	const struct drm_connector_helper_funcs *funcs =
> > +		connector->base.helper_private;
> > +	int ret;
> > +
> > +	if (funcs->cleanup_writeback_job) {
> > +		ret = funcs->prepare_writeback_job(connector, job);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	job->prepared = true;
> > +	return 0;
> > +}
> > +
> >  /**
> >   * drm_writeback_queue_job - Queue a writeback job for later signalling
> >   * @wb_connector: The writeback connector to queue a job on
> > @@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
> >  
> >  void drm_writeback_cleanup_job(struct drm_writeback_job *job)
> >  {
> > +	struct drm_writeback_connector *connector = job->connector;
> > +	const struct drm_connector_helper_funcs *funcs =
> > +		connector->base.helper_private;
> > +
> > +	if (job->prepared && funcs->cleanup_writeback_job)
> > +		funcs->cleanup_writeback_job(connector, job);
> > +
> >  	if (job->fb)
> >  		drm_framebuffer_put(job->fb);
> >  
> > diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> > index 61142aa0ab23..73d03fe66799 100644
> > --- a/include/drm/drm_modeset_helper_vtables.h
> > +++ b/include/drm/drm_modeset_helper_vtables.h
> > @@ -49,6 +49,8 @@
> >   */
> >  
> >  enum mode_set_atomic;
> > +struct drm_writeback_connector;
> > +struct drm_writeback_job;
> >  
> >  /**
> >   * struct drm_crtc_helper_funcs - helper operations for CRTCs
> > @@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
> >  	 */
> >  	void (*atomic_commit)(struct drm_connector *connector,
> >  			      struct drm_connector_state *state);
> > +
> > +	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
> > +				     struct drm_writeback_job *job);
> > +	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
> > +				      struct drm_writeback_job *job);
> >  };
> >  
> >  /**
> > diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> > index 47662c362743..777c14c847f0 100644
> > --- a/include/drm/drm_writeback.h
> > +++ b/include/drm/drm_writeback.h
> > @@ -79,6 +79,20 @@ struct drm_writeback_connector {
> >  };
> >  
> >  struct drm_writeback_job {
> > +	/**
> > +	 * @connector:
> > +	 *
> > +	 * Back-pointer to the writeback connector associated with the job
> > +	 */
> > +	struct drm_writeback_connector *connector;
> 
> It kind-of feels like this shouldn't be necessary, but I think
> avoiding it would mean either allocating the work_struct outside of
> the job, and tracking the connector there instead of in the job, or
> having two calls to unprepare - one in signal_completion and one in
> destroy_state.

I don't like the second option as unprepare could potentially be costly,
and should thus not be called from signal_completion that may run in
interrupt context. The first option is doable, but I think it will
result in even worse code.

> It's probably not worth the gymnastics to avoid the backpointer... but
> for some reason the backpointer feels intangibly dirty to me.

A writeback job exists in the context of a writeback connector, why do
you feel the backpointer is dirty ?

> > +
> > +	/**
> > +	 * @prepared:
> > +	 *
> > +	 * Set when the job has been prepared with drm_writeback_prepare_job()
> > +	 */
> > +	bool prepared;
> > +
> >  	/**
> >  	 * @cleanup_work:
> >  	 *
> > @@ -98,7 +112,7 @@ struct drm_writeback_job {
> >  	 * @fb:
> >  	 *
> >  	 * Framebuffer to be written to by the writeback connector. Do not set
> > -	 * directly, use drm_atomic_set_writeback_fb_for_connector()
> > +	 * directly, use drm_writeback_set_fb()
> >  	 */
> >  	struct drm_framebuffer *fb;
> >  
> > @@ -108,6 +122,13 @@ struct drm_writeback_job {
> >  	 * Fence which will signal once the writeback has completed
> >  	 */
> >  	struct dma_fence *out_fence;
> > +
> > +	/**
> > +	 * @priv:
> > +	 *
> > +	 * Driver-private data
> > +	 */
> > +	void *priv;
> >  };
> >  
> >  static inline struct drm_writeback_connector *
> > @@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >  				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> >  				 const u32 *formats, int n_formats);
> >  
> > +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> > +			 struct drm_framebuffer *fb);
> > +
> > +int drm_writeback_prepare_job(struct drm_writeback_job *job);
> > +
> >  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> >  			     struct drm_connector_state *conn_state);
> >  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 21:56       ` Laurent Pinchart
@ 2019-02-22 13:33         ` Brian Starkey
  0 siblings, 0 replies; 65+ messages in thread
From: Brian Starkey @ 2019-02-22 13:33 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Thu, Feb 21, 2019 at 11:56:09PM +0200, Laurent Pinchart wrote:
> Hi Brian,
> 
> On Thu, Feb 21, 2019 at 04:02:37PM +0000, Brian Starkey wrote:
> > On Thu, Feb 21, 2019 at 12:42:25PM +0200, Laurent Pinchart wrote:
> > > On Thu, Feb 21, 2019 at 12:32:05PM +0200, Laurent Pinchart wrote:
> > >> The drm_writeback_queue_job() function takes ownership of the passed job
> > >> and requires the caller to manually set the connector state
> > >> writeback_job pointer to NULL. To simplify drivers and avoid errors
> > >> (such as the missing NULL set in the vc4 driver), pass the connector
> > >> state pointer to the function instead of the job pointer, and set the
> > >> writeback_job pointer to NULL internally.
> > 
> > LGTM, it was a mistake not doing it like this from the start.
> > 
> > I do have one suggestion below, but either way:
> > 
> > Reviewed-by: Brian Starkey <brian.starkey@arm.com>
> > 
> > >> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > >> ---
> > >>  drivers/gpu/drm/arm/malidp_mw.c |  3 +--
> > >>  drivers/gpu/drm/drm_writeback.c | 15 ++++++++++-----
> > >>  drivers/gpu/drm/vc4/vc4_txp.c   |  2 +-
> > >>  include/drm/drm_writeback.h     |  2 +-
> > >>  4 files changed, 13 insertions(+), 9 deletions(-)
> > >> 
> > >> diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
> > >> index 041a64dc7167..87627219ce3b 100644
> > >> --- a/drivers/gpu/drm/arm/malidp_mw.c
> > >> +++ b/drivers/gpu/drm/arm/malidp_mw.c
> > >> @@ -252,8 +252,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm,
> > >>  				     &mw_state->addrs[0],
> > >>  				     mw_state->format);
> > >>  
> > >> -		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
> > >> -		conn_state->writeback_job = NULL;
> > >> +		drm_writeback_queue_job(mw_conn, conn_state);
> > >>  		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
> > >>  					   mw_state->pitches, mw_state->n_planes,
> > >>  					   fb->width, fb->height, mw_state->format,
> > >> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> > >> index c20e6fe00cb3..338b993d7c9f 100644
> > >> --- a/drivers/gpu/drm/drm_writeback.c
> > >> +++ b/drivers/gpu/drm/drm_writeback.c
> > >> @@ -242,11 +242,12 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
> > >>  /**
> > >>   * drm_writeback_queue_job - Queue a writeback job for later signalling
> > >>   * @wb_connector: The writeback connector to queue a job on
> > >> - * @job: The job to queue
> > >> + * @conn_state: The connector state containing the job to queue
> > >>   *
> > >> - * This function adds a job to the job_queue for a writeback connector. It
> > >> - * should be considered to take ownership of the writeback job, and so any other
> > >> - * references to the job must be cleared after calling this function.
> > >> + * This function adds the job contained in @conn_state to the job_queue for a
> > >> + * writeback connector. It takes ownership of the writeback job and sets the
> > >> + * @conn_state->writeback_job to NULL, and so no access to the job may be
> > >> + * performed by the caller after this function returns.
> > >>   *
> > >>   * Drivers must ensure that for a given writeback connector, jobs are queued in
> > >>   * exactly the same order as they will be completed by the hardware (and
> > >> @@ -258,10 +259,14 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
> > >>   * See also: drm_writeback_signal_completion()
> > >>   */
> > >>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> > >> -			     struct drm_writeback_job *job)
> > >> +			     struct drm_connector_state *conn_state)
> > >>  {
> > >> +	struct drm_writeback_job *job;
> > >>  	unsigned long flags;
> > >>  
> > >> +	job = conn_state->writeback_job;
> > 
> > What do you think about adding a defensive WARN_ON(!job)?
> 
> I expect this function to be called from an atomic commit handler for
> the writeback job, which will need to access the job's contents (in
> particular the framebuffer). I thus think we'll never be called with a
> NULL job here, so a WARN_ON would only consume a bit of CPU time and
> memory without adding any safeguard. If your analysis differs and you
> still think there's a risk, I'll add it.

My thinking was that it's up to drivers to make the check for a job,
and that a driver might get that wrong (and bring down the kernel).
With the old signature, it was entirely obvious that you needed a job
as it was one of the arguments to the function. Now it _should_ be
obvious from the naming, but it's not explicit anymore.

Anyway, I don't feel strongly about it. You'd hope a buggy driver
would hit the panic almost immediately during development and never do
any harm.

-Brian

> 
> > >> +	conn_state->writeback_job = NULL;
> > >> +
> > >>  	spin_lock_irqsave(&wb_connector->job_lock, flags);
> > >>  	list_add_tail(&job->list_entry, &wb_connector->job_queue);
> > >>  	spin_unlock_irqrestore(&wb_connector->job_lock, flags);
> > >> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> > >> index aa279b5b0de7..5dabd91f2d7e 100644
> > >> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> > >> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> > >> @@ -327,7 +327,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
> > >>  
> > >>  	TXP_WRITE(TXP_DST_CTRL, ctrl);
> > >>  
> > >> -	drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
> > >> +	drm_writeback_queue_job(&txp->connector, conn_state);
> > >>  }
> > >>  
> > >>  static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
> > >> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> > >> index 23df9d463003..47662c362743 100644
> > >> --- a/include/drm/drm_writeback.h
> > >> +++ b/include/drm/drm_writeback.h
> > >> @@ -123,7 +123,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
> > >>  				 const u32 *formats, int n_formats);
> > >>  
> > >>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> > >> -			     struct drm_writeback_job *job);
> > >> +			     struct drm_connector_state *conn_state);
> > >>  
> > >>  void drm_writeback_cleanup_job(struct drm_writeback_job *job);
> > >>  
> 
> -- 
> Regards,
> 
> Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-21 22:12     ` Laurent Pinchart
@ 2019-02-22 13:50       ` Brian Starkey
  2019-02-22 14:49         ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-02-22 13:50 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Fri, Feb 22, 2019 at 12:12:00AM +0200, Laurent Pinchart wrote:
> Hi Brian,
> 
> On Thu, Feb 21, 2019 at 06:12:03PM +0000, Brian Starkey wrote:
> > On Thu, Feb 21, 2019 at 12:32:07PM +0200, Laurent Pinchart wrote:
> > > As writeback jobs contain a framebuffer, drivers may need to prepare and
> > > cleanup them the same way they can prepare and cleanup framebuffers for
> > > planes. Add two new optional connector helper operations,
> > > .prepare_writeback_job() and .cleanup_writeback_job() to support this.
> > > 
> > > The job prepare operation is called from
> > > drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
> > > that would need to be called by all drivers not using
> > > drm_atomic_helper_commit(). The job cleanup operation is called from the
> > > existing drm_writeback_cleanup_job() function, invoked both when
> > > destroying the job as part of a aborted commit, or when the job
> > > completes.
> > > 
> > > The drm_writeback_job structure is extended with a priv field to let
> > > drivers store per-job data, such as mappings related to the writeback
> > > framebuffer.
> > > 
> > > For internal plumbing reasons the drm_writeback_job structure needs to
> > > store a back-pointer to the drm_writeback_connector. To avoid pushing
> > > too much writeback-specific knowledge to drm_atomic_uapi.c, create a
> > > drm_writeback_set_fb() function, move the writeback job setup code
> > > there, and set the connector backpointer. The prepare_signaling()
> > > function doesn't need to allocate writeback jobs and can ignore
> > > connectors without a job, as it is called after the writeback jobs are
> > > allocated to store framebuffers, and a writeback fence with a
> > > framebuffer is an invalid configuration that gets rejected by the commit
> > > check.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > 
> > I probably need to revisit this with fresh eyes tomorrow, but how come
> > the prepared-ness and whatever is being prepared can't be put into a
> > subclassed framebuffer? That seems more natural to me, than tracking
> > it with the job. It's really the framebuffer we're preparing after
> > all.
> 
> In my patch series I indeed need to track information related to the
> framebuffer only, but is it a good idea to limit writeback
> prepare/cleanup to that ? Other drivers may have different needs.

IMO better to write the code for the case we know, rather than
speculate.

> 
> Furthermore, the mapping needs to exist for the lifetime of the
> writeback job, so I'd have to add a counter to the framebuffer subclass
> to track these too. While doable, it starts sounding a bit like a hack,
> and may create race conditions.
> 

You're probably right. As you were saying to Daniel - writeback things
don't have the same lifetime as the state, so the job is the only
place to store things which would normally go in the state.

> > I guess if you have the same framebuffer in use for multiple things,
> > you might need to track it separately? Can the mappings be shared in
> > that case?
> 
> Speaking about my case, the mapping is per-CRTC, so if I use the same
> framebuffer for multiple CRTCs I'd need separate mappings. Other drivers
> may have simpler or more complex needs.
> 
> When you'll revisit this with fresh eyes, I'd like to know if you think
> it would be worth it replacing the priv pointer with allocate/destroy
> operations to allow subclassing the writeback job. We could possibly do
> without the destroy operation if we mandate embedding the writeback job
> as the first field of the subclass and usage of kmalloc (& friends) to
> allocate the subclass structure. We could even do without the allocate
> operation if we just stored the size of the subclass in the writeback
> connector itself, but that would depart from what we usually do in DRM.
> 

I think enabling the job to be subclassed would be a fine solution,
with functions which are as close as possible to the other DRM
objects.

I'm not sure there's a lot of advantage in skipping allocate/destroy;
it should be fine to just have helpers for the common case.

I wonder if renaming it to "writeback_state" instead of
"writeback_job" is too confusing (because it doesn't _quite_ act like
the other states, even though its purpose is very similar).

> I would also like to know if I should create a writeback connector
> operations structure to store the prepare/cleanup and perhaps
> allocate/destroy operations instead of adding them to the connector
> helper funcs.

I think just adding the functions to the existing connector
operations is overall less clutter, but I don't mind either way.

-Brian

> 
> > > ---
> > >  drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
> > >  drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
> > >  drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
> > >  include/drm/drm_modeset_helper_vtables.h |  7 ++++
> > >  include/drm/drm_writeback.h              | 28 ++++++++++++++-
> > >  5 files changed, 96 insertions(+), 24 deletions(-)
> > > 
> > > This patch is currently missing documentation for the
> > > .prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
> > > to fix this, but first wanted feedback on the direction taken. I'm not
> > > entirely happy with the priv pointer in the drm_writeback_job structure,
> > > but adding a full state duplicate/destroy machinery for that structure
> > > was equally unappealing to me.
> > > 
> > > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> > > index 6fe2303fccd9..70a4886c6e65 100644
> > > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > > @@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
> > >  int drm_atomic_helper_prepare_planes(struct drm_device *dev,
> > >  				     struct drm_atomic_state *state)
> > >  {
> > > +	struct drm_connector *connector;
> > > +	struct drm_connector_state *new_conn_state;
> > >  	struct drm_plane *plane;
> > >  	struct drm_plane_state *new_plane_state;
> > >  	int ret, i, j;
> > >  
> > > +	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
> > > +		if (!new_conn_state->writeback_job)
> > > +			continue;
> > > +
> > > +		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
> > > +		if (ret < 0)
> > > +			return ret;
> > > +	}
> > > +
> > >  	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
> > >  		const struct drm_plane_helper_funcs *funcs;
> > >  
> > > diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
> > > index c40889888a16..e802152a01ad 100644
> > > --- a/drivers/gpu/drm/drm_atomic_uapi.c
> > > +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> > > @@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> > >  	return 0;
> > >  }
> > >  
> > > -static struct drm_writeback_job *
> > > -drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
> > > -{
> > > -	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> > > -
> > > -	if (!conn_state->writeback_job)
> > > -		conn_state->writeback_job =
> > > -			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> > > -
> > > -	return conn_state->writeback_job;
> > > -}
> > > -
> > >  static int drm_atomic_set_writeback_fb_for_connector(
> > >  		struct drm_connector_state *conn_state,
> > >  		struct drm_framebuffer *fb)
> > >  {
> > > -	struct drm_writeback_job *job =
> > > -		drm_atomic_get_writeback_job(conn_state);
> > > -	if (!job)
> > > -		return -ENOMEM;
> > > +	int ret;
> > >  
> > > -	drm_framebuffer_assign(&job->fb, fb);
> > > +	ret = drm_writeback_set_fb(conn_state, fb);
> > > +	if (ret < 0)
> > > +		return ret;
> > >  
> > >  	if (fb)
> > >  		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
> > > @@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
> > >  
> > >  	for_each_new_connector_in_state(state, conn, conn_state, i) {
> > >  		struct drm_writeback_connector *wb_conn;
> > > -		struct drm_writeback_job *job;
> > >  		struct drm_out_fence_state *f;
> > >  		struct dma_fence *fence;
> > >  		s32 __user *fence_ptr;
> > >  
> > > +		if (!conn_state->writeback_job)
> > > +			continue;
> > > +
> > >  		fence_ptr = get_out_fence_for_connector(state, conn);
> > >  		if (!fence_ptr)
> > >  			continue;
> > >  
> > > -		job = drm_atomic_get_writeback_job(conn_state);
> > > -		if (!job)
> > > -			return -ENOMEM;
> > > -
> > >  		f = krealloc(*fence_state, sizeof(**fence_state) *
> > >  			     (*num_fences + 1), GFP_KERNEL);
> > >  		if (!f)
> > > @@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
> > >  			return ret;
> > >  		}
> > >  
> > > -		job->out_fence = fence;
> > > +		conn_state->writeback_job->out_fence = fence;
> > >  	}
> > >  
> > >  	/*
> > > diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> > > index afb1ae6e0ecb..4678d61d634a 100644
> > > --- a/drivers/gpu/drm/drm_writeback.c
> > > +++ b/drivers/gpu/drm/drm_writeback.c
> > > @@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
> > >  }
> > >  EXPORT_SYMBOL(drm_writeback_connector_init);
> > >  
> > > +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> > > +			 struct drm_framebuffer *fb)
> > > +{
> > > +	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> > > +
> > > +	if (!conn_state->writeback_job) {
> > > +		conn_state->writeback_job =
> > > +			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> > > +		if (!conn_state->writeback_job)
> > > +			return -ENOMEM;
> > > +
> > > +		conn_state->writeback_job->connector =
> > > +			drm_connector_to_writeback(conn_state->connector);
> > > +	}
> > > +
> > > +	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
> > > +	return 0;
> > > +}
> > > +
> > > +int drm_writeback_prepare_job(struct drm_writeback_job *job)
> > > +{
> > > +	struct drm_writeback_connector *connector = job->connector;
> > > +	const struct drm_connector_helper_funcs *funcs =
> > > +		connector->base.helper_private;
> > > +	int ret;
> > > +
> > > +	if (funcs->cleanup_writeback_job) {
> > > +		ret = funcs->prepare_writeback_job(connector, job);
> > > +		if (ret < 0)
> > > +			return ret;
> > > +	}
> > > +
> > > +	job->prepared = true;
> > > +	return 0;
> > > +}
> > > +
> > >  /**
> > >   * drm_writeback_queue_job - Queue a writeback job for later signalling
> > >   * @wb_connector: The writeback connector to queue a job on
> > > @@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
> > >  
> > >  void drm_writeback_cleanup_job(struct drm_writeback_job *job)
> > >  {
> > > +	struct drm_writeback_connector *connector = job->connector;
> > > +	const struct drm_connector_helper_funcs *funcs =
> > > +		connector->base.helper_private;
> > > +
> > > +	if (job->prepared && funcs->cleanup_writeback_job)
> > > +		funcs->cleanup_writeback_job(connector, job);
> > > +
> > >  	if (job->fb)
> > >  		drm_framebuffer_put(job->fb);
> > >  
> > > diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> > > index 61142aa0ab23..73d03fe66799 100644
> > > --- a/include/drm/drm_modeset_helper_vtables.h
> > > +++ b/include/drm/drm_modeset_helper_vtables.h
> > > @@ -49,6 +49,8 @@
> > >   */
> > >  
> > >  enum mode_set_atomic;
> > > +struct drm_writeback_connector;
> > > +struct drm_writeback_job;
> > >  
> > >  /**
> > >   * struct drm_crtc_helper_funcs - helper operations for CRTCs
> > > @@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
> > >  	 */
> > >  	void (*atomic_commit)(struct drm_connector *connector,
> > >  			      struct drm_connector_state *state);
> > > +
> > > +	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
> > > +				     struct drm_writeback_job *job);
> > > +	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
> > > +				      struct drm_writeback_job *job);
> > >  };
> > >  
> > >  /**
> > > diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> > > index 47662c362743..777c14c847f0 100644
> > > --- a/include/drm/drm_writeback.h
> > > +++ b/include/drm/drm_writeback.h
> > > @@ -79,6 +79,20 @@ struct drm_writeback_connector {
> > >  };
> > >  
> > >  struct drm_writeback_job {
> > > +	/**
> > > +	 * @connector:
> > > +	 *
> > > +	 * Back-pointer to the writeback connector associated with the job
> > > +	 */
> > > +	struct drm_writeback_connector *connector;
> > 
> > It kind-of feels like this shouldn't be necessary, but I think
> > avoiding it would mean either allocating the work_struct outside of
> > the job, and tracking the connector there instead of in the job, or
> > having two calls to unprepare - one in signal_completion and one in
> > destroy_state.
> 
> I don't like the second option as unprepare could potentially be costly,
> and should thus not be called from signal_completion that may run in
> interrupt context. The first option is doable, but I think it will
> result in even worse code.
> 
> > It's probably not worth the gymnastics to avoid the backpointer... but
> > for some reason the backpointer feels intangibly dirty to me.
> 
> A writeback job exists in the context of a writeback connector, why do
> you feel the backpointer is dirty ?
> 
> > > +
> > > +	/**
> > > +	 * @prepared:
> > > +	 *
> > > +	 * Set when the job has been prepared with drm_writeback_prepare_job()
> > > +	 */
> > > +	bool prepared;
> > > +
> > >  	/**
> > >  	 * @cleanup_work:
> > >  	 *
> > > @@ -98,7 +112,7 @@ struct drm_writeback_job {
> > >  	 * @fb:
> > >  	 *
> > >  	 * Framebuffer to be written to by the writeback connector. Do not set
> > > -	 * directly, use drm_atomic_set_writeback_fb_for_connector()
> > > +	 * directly, use drm_writeback_set_fb()
> > >  	 */
> > >  	struct drm_framebuffer *fb;
> > >  
> > > @@ -108,6 +122,13 @@ struct drm_writeback_job {
> > >  	 * Fence which will signal once the writeback has completed
> > >  	 */
> > >  	struct dma_fence *out_fence;
> > > +
> > > +	/**
> > > +	 * @priv:
> > > +	 *
> > > +	 * Driver-private data
> > > +	 */
> > > +	void *priv;
> > >  };
> > >  
> > >  static inline struct drm_writeback_connector *
> > > @@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
> > >  				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> > >  				 const u32 *formats, int n_formats);
> > >  
> > > +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> > > +			 struct drm_framebuffer *fb);
> > > +
> > > +int drm_writeback_prepare_job(struct drm_writeback_job *job);
> > > +
> > >  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> > >  			     struct drm_connector_state *conn_state);
> > >  
> 
> -- 
> Regards,
> 
> Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 00/19] R-Car DU display writeback support
  2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
                   ` (18 preceding siblings ...)
  2019-02-21 10:32 ` [PATCH v5 19/19] drm: rcar-du: Add writeback support for R-Car Gen3 Laurent Pinchart
@ 2019-02-22 14:04 ` Brian Starkey
  2019-02-22 14:47   ` Laurent Pinchart
  19 siblings, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-02-22 14:04 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Liviu Dudau, nd, james qian wang (Arm Technology China),
	Kieran Bingham, dri-devel

Hi,

On Thu, Feb 21, 2019 at 12:31:53PM +0200, Laurent Pinchart wrote:
> Hello everybody,
> 
> This patch series implements display writeback support for the R-Car
> Gen3 platforms in the VSP1 and DU drivers.
> 
> Patches 01/19 to 11/19 prepare the VSP1 driver for writeback support
> with all the necessary plumbing, including extensions of the API between
> the VSP1 and DU drivers.
> 
> Compared to v4 the major change is the move from V4L2 to DRM writeback
> connectors for the userspace API. This has caused a few issues with
> writeback support to be uncovered, and they are addressed by patches
> 12/19 to 14/19. Patch 15/19 is an unrelated drive-by fix.
> 
> Patches 16/19 to 18/19 then perform refactoring of the DU driver, to
> finally add writeback support in patch 19/19.
> 
> The writeback pixel format is restricted to RGB, due to the VSP1
> outputting RGB to the display and lacking a separate colour space
> conversion unit for writeback. The resolution can be freely picked by
> will result in cropping or composing, not scaling.

I think this sentence about cropping doesn't match the implementation,
as you're checking the framebuffer size against the mode.

We want to enable scaling/composing on writeback (which our HW can
do). I thought we'd add WRITEBACK_X/Y/W/H properties to set the
destination buffer coordinates. One of our team is working on that
now.

We don't have cropping, but I thought that could be achieved by
exposing CRTC_X/Y/W/H on writeback connectors.

Thanks,
-Brian

> 
> Writeback requests are queued to the hardware on page flip (atomic
> flush), and complete at the next vblank. This means that a queued
> writeback buffer will not be processed until the next page flip, but
> once it starts being written to by the VSP, it will complete at the next
> vblank regardless of whether another page flip occurs at that time.
> 
> The code is based on a merge of the media master branch, the drm-next
> branch and the R-Car DT next branch. For convenience patches can be
> found at
> 
>         git://linuxtv.org/pinchartl/media.git v4l2/vsp1/writeback
> 
> Kieran Bingham (1):
>   Revert "[media] v4l: vsp1: Supply frames to the DU continuously"
> 
> Laurent Pinchart (18):
>   media: vsp1: wpf: Fix partition configuration for display pipelines
>   media: vsp1: Replace leftover occurrence of fragment with body
>   media: vsp1: Fix addresses of display-related registers for VSP-DL
>   media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse
>   media: vsp1: Replace the display list internal flag with a flags field
>   media: vsp1: dl: Support one-shot entries in the display list
>   media: vsp1: wpf: Add writeback support
>   media: vsp1: drm: Split RPF format setting to separate function
>   media: vsp1: drm: Extend frame completion API to the DU driver
>   media: vsp1: drm: Implement writeback support
>   drm: writeback: Cleanup job ownership handling when queuing job
>   drm: writeback: Fix leak of writeback job
>   drm: writeback: Add job prepare and cleanup operations
>   drm/msm: Remove prototypes for non-existing functions
>   drm: rcar-du: Fix rcar_du_crtc structure documentation
>   drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure
>   drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate
>     functions
>   drm: rcar-du: Add writeback support for R-Car Gen3
> 
>  drivers/gpu/drm/arm/malidp_mw.c             |   3 +-
>  drivers/gpu/drm/drm_atomic_helper.c         |  11 ++
>  drivers/gpu/drm/drm_atomic_state_helper.c   |   4 +
>  drivers/gpu/drm/drm_atomic_uapi.c           |  31 +--
>  drivers/gpu/drm/drm_writeback.c             |  71 ++++++-
>  drivers/gpu/drm/msm/msm_drv.h               |   2 -
>  drivers/gpu/drm/rcar-du/Kconfig             |   4 +
>  drivers/gpu/drm/rcar-du/Makefile            |   3 +-
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.h      |   9 +-
>  drivers/gpu/drm/rcar-du/rcar_du_kms.c       |  37 ++++
>  drivers/gpu/drm/rcar-du/rcar_du_kms.h       |   1 +
>  drivers/gpu/drm/rcar-du/rcar_du_vsp.c       | 121 ++++++------
>  drivers/gpu/drm/rcar-du/rcar_du_vsp.h       |  17 ++
>  drivers/gpu/drm/rcar-du/rcar_du_writeback.c | 203 ++++++++++++++++++++
>  drivers/gpu/drm/rcar-du/rcar_du_writeback.h |  39 ++++
>  drivers/gpu/drm/vc4/vc4_txp.c               |   2 +-
>  drivers/media/platform/vsp1/vsp1_dl.c       | 127 ++++++++++--
>  drivers/media/platform/vsp1/vsp1_dl.h       |   8 +-
>  drivers/media/platform/vsp1/vsp1_drm.c      |  92 ++++++---
>  drivers/media/platform/vsp1/vsp1_drm.h      |   2 +-
>  drivers/media/platform/vsp1/vsp1_drv.c      |  15 ++
>  drivers/media/platform/vsp1/vsp1_pipe.c     |   5 +
>  drivers/media/platform/vsp1/vsp1_pipe.h     |   1 +
>  drivers/media/platform/vsp1/vsp1_regs.h     |   6 +-
>  drivers/media/platform/vsp1/vsp1_rwpf.h     |   2 +
>  drivers/media/platform/vsp1/vsp1_video.c    |  49 +++--
>  drivers/media/platform/vsp1/vsp1_wpf.c      |  68 +++++--
>  include/drm/drm_modeset_helper_vtables.h    |   7 +
>  include/drm/drm_writeback.h                 |  30 ++-
>  include/media/vsp1.h                        |  19 +-
>  30 files changed, 790 insertions(+), 199 deletions(-)
>  create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.c
>  create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.h
> 
> -- 
> Regards,
> 
> Laurent Pinchart
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-02-21 10:32 ` [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list Laurent Pinchart
  2019-02-21 13:16   ` Kieran Bingham
@ 2019-02-22 14:30   ` Brian Starkey
  2019-02-22 14:46     ` Laurent Pinchart
  1 sibling, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-02-22 14:30 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Liviu Dudau, nd, james qian wang (Arm Technology China),
	Kieran Bingham, dri-devel

Hi Laurent,

On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> One-shot entries are used as an alternative to committing a complete new
> display list when a couple of registers need to be written for one frame
> and then reset to another value for all subsequent frames. This will be
> used to implement writeback support that will need to enable writeback
> for the duration of a single frame.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
>  2 files changed, 81 insertions(+)
> 
> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> index 886b3a69d329..7b4d252bfde7 100644
> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
>  
>  	unsigned int num_entries;
>  	unsigned int max_entries;
> +
> +	unsigned int num_patches;
> +	struct {
> +		struct vsp1_dl_entry *entry;
> +		u32 data;
> +	} patches[2];
>  };
>  
>  /**
> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
>  		return;
>  
>  	dlb->num_entries = 0;
> +	dlb->num_patches = 0;
>  
>  	spin_lock_irqsave(&dlb->pool->lock, flags);
>  	list_add_tail(&dlb->free, &dlb->pool->free);
> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
>  	dlb->num_entries++;
>  }
>  
> +/**
> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> + *	single frame
> + * @dlb: The body
> + * @reg: The register address
> + * @value: The register value
> + * @reset_value: The value to reset the register to at the next vblank
> + *
> + * Display lists in continuous mode are re-used by the hardware for successive
> + * frames until a new display list is committed. Changing the VSP configuration
> + * normally requires creating and committing a new display list. This function
> + * offers an alternative race-free way by writing a @value to the @register in
> + * the display list body for a single frame, specifying in @reset_value the
> + * value to reset the register to one vblank after the display list is
> + * committed.
> + *
> + * The maximum number of one-shot entries is limited to 2 per display list body,
> + * and one-shot entries are counted in the total number of entries specified
> + * when the body is allocated by vsp1_dl_body_alloc().
> + */
> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> +				u32 reset_value)
> +{
> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> +		return;
> +
> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> +		      "DLB patches size exceeded (max %zu)",
> +		      ARRAY_SIZE(dlb->patches)))
> +		return;
> +
> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> +	dlb->patches[dlb->num_patches].data = reset_value;
> +	dlb->num_patches++;
> +
> +	dlb->entries[dlb->num_entries].addr = reg;
> +	dlb->entries[dlb->num_entries].data = value;
> +	dlb->num_entries++;
> +}
> +
>  /* -----------------------------------------------------------------------------
>   * Display List Extended Command Management
>   */
> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
>  	 * has at least one body, thus we reinitialise the entries list.
>  	 */
>  	dl->body0->num_entries = 0;
> +	dl->body0->num_patches = 0;
>  
>  	list_add_tail(&dl->list, &dl->dlm->free);
>  }
> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
>   * Display List Manager
>   */
>  
> +/**
> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> + *	interrupt
> + * @dlm: the display list manager
> + *
> + * Apply all one-shot patches registered for the active display list.
> + */
> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> +{
> +	struct vsp1_dl_body *dlb;
> +	struct vsp1_dl_list *dl;
> +	unsigned int i;
> +
> +	spin_lock(&dlm->lock);
> +
> +	dl = dlm->active;
> +	if (!dl)
> +		goto done;
> +
> +	list_for_each_entry(dlb, &dl->bodies, list) {
> +		for (i = 0; i < dlb->num_patches; ++i)
> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> +		dlb->num_patches = 0;
> +	}
> +
> +done:
> +	spin_unlock(&dlm->lock);
> +}
> +

We've got some HW which doesn't support one-shot writeback, and use a
similar trick to try and disable writeback immediately after the flip.

We ran into issues where the "start" interrupt wouldn't run in time to
make sure the writeback disable was committed before the next frame.
We have to keep track of whether the disable really happened in time,
before we release the output buffer.

Might you have a similar problem here?

Thanks,
-Brian

>  /**
>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
>   * @dlm: the display list manager
> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> index e0fdb145e6ed..f845607abc4c 100644
> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
>  					unsigned int prealloc);
>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
>  
> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
>  
>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> +				u32 reset_value);
>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
>  
> -- 
> Regards,
> 
> Laurent Pinchart
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-02-22 14:30   ` Brian Starkey
@ 2019-02-22 14:46     ` Laurent Pinchart
  2019-02-22 15:06       ` Brian Starkey
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-22 14:46 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > One-shot entries are used as an alternative to committing a complete new
> > display list when a couple of registers need to be written for one frame
> > and then reset to another value for all subsequent frames. This will be
> > used to implement writeback support that will need to enable writeback
> > for the duration of a single frame.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> >  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> >  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> >  2 files changed, 81 insertions(+)
> > 
> > diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > index 886b3a69d329..7b4d252bfde7 100644
> > --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> >  
> >  	unsigned int num_entries;
> >  	unsigned int max_entries;
> > +
> > +	unsigned int num_patches;
> > +	struct {
> > +		struct vsp1_dl_entry *entry;
> > +		u32 data;
> > +	} patches[2];
> >  };
> >  
> >  /**
> > @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> >  		return;
> >  
> >  	dlb->num_entries = 0;
> > +	dlb->num_patches = 0;
> >  
> >  	spin_lock_irqsave(&dlb->pool->lock, flags);
> >  	list_add_tail(&dlb->free, &dlb->pool->free);
> > @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> >  	dlb->num_entries++;
> >  }
> >  
> > +/**
> > + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > + *	single frame
> > + * @dlb: The body
> > + * @reg: The register address
> > + * @value: The register value
> > + * @reset_value: The value to reset the register to at the next vblank
> > + *
> > + * Display lists in continuous mode are re-used by the hardware for successive
> > + * frames until a new display list is committed. Changing the VSP configuration
> > + * normally requires creating and committing a new display list. This function
> > + * offers an alternative race-free way by writing a @value to the @register in
> > + * the display list body for a single frame, specifying in @reset_value the
> > + * value to reset the register to one vblank after the display list is
> > + * committed.
> > + *
> > + * The maximum number of one-shot entries is limited to 2 per display list body,
> > + * and one-shot entries are counted in the total number of entries specified
> > + * when the body is allocated by vsp1_dl_body_alloc().
> > + */
> > +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > +				u32 reset_value)
> > +{
> > +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > +		      "DLB size exceeded (max %u)", dlb->max_entries))
> > +		return;
> > +
> > +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > +		      "DLB patches size exceeded (max %zu)",
> > +		      ARRAY_SIZE(dlb->patches)))
> > +		return;
> > +
> > +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > +	dlb->patches[dlb->num_patches].data = reset_value;
> > +	dlb->num_patches++;
> > +
> > +	dlb->entries[dlb->num_entries].addr = reg;
> > +	dlb->entries[dlb->num_entries].data = value;
> > +	dlb->num_entries++;
> > +}
> > +
> >  /* -----------------------------------------------------------------------------
> >   * Display List Extended Command Management
> >   */
> > @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> >  	 * has at least one body, thus we reinitialise the entries list.
> >  	 */
> >  	dl->body0->num_entries = 0;
> > +	dl->body0->num_patches = 0;
> >  
> >  	list_add_tail(&dl->list, &dl->dlm->free);
> >  }
> > @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> >   * Display List Manager
> >   */
> >  
> > +/**
> > + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > + *	interrupt
> > + * @dlm: the display list manager
> > + *
> > + * Apply all one-shot patches registered for the active display list.
> > + */
> > +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > +{
> > +	struct vsp1_dl_body *dlb;
> > +	struct vsp1_dl_list *dl;
> > +	unsigned int i;
> > +
> > +	spin_lock(&dlm->lock);
> > +
> > +	dl = dlm->active;
> > +	if (!dl)
> > +		goto done;
> > +
> > +	list_for_each_entry(dlb, &dl->bodies, list) {
> > +		for (i = 0; i < dlb->num_patches; ++i)
> > +			dlb->patches[i].entry->data = dlb->patches[i].data;
> > +		dlb->num_patches = 0;
> > +	}
> > +
> > +done:
> > +	spin_unlock(&dlm->lock);
> > +}
> > +
> 
> We've got some HW which doesn't support one-shot writeback, and use a
> similar trick to try and disable writeback immediately after the flip.
> 
> We ran into issues where the "start" interrupt wouldn't run in time to
> make sure the writeback disable was committed before the next frame.
> We have to keep track of whether the disable really happened in time,
> before we release the output buffer.
> 
> Might you have a similar problem here?

We may, but there's no provision at the hardware level to check if the
configuration updated happened in time. I could add some safety checks
but I believe they would be racy in the best case :-(

Note that we have the duration of a complete frame to disable writeback,
as we receive an interrupt when the frame starts, and have until vblank
to update the configuration. It's thus slightly better than having to
disable writeback between vblank and the start of the next frame.

> >  /**
> >   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> >   * @dlm: the display list manager
> > diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > index e0fdb145e6ed..f845607abc4c 100644
> > --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> >  					unsigned int prealloc);
> >  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> >  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> >  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> >  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> >  
> > @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> >  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> >  
> >  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > +				u32 reset_value);
> >  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> >  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> >  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 00/19] R-Car DU display writeback support
  2019-02-22 14:04 ` [PATCH v5 00/19] R-Car DU display writeback support Brian Starkey
@ 2019-02-22 14:47   ` Laurent Pinchart
  0 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-22 14:47 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Fri, Feb 22, 2019 at 02:04:10PM +0000, Brian Starkey wrote:
> On Thu, Feb 21, 2019 at 12:31:53PM +0200, Laurent Pinchart wrote:
> > Hello everybody,
> > 
> > This patch series implements display writeback support for the R-Car
> > Gen3 platforms in the VSP1 and DU drivers.
> > 
> > Patches 01/19 to 11/19 prepare the VSP1 driver for writeback support
> > with all the necessary plumbing, including extensions of the API between
> > the VSP1 and DU drivers.
> > 
> > Compared to v4 the major change is the move from V4L2 to DRM writeback
> > connectors for the userspace API. This has caused a few issues with
> > writeback support to be uncovered, and they are addressed by patches
> > 12/19 to 14/19. Patch 15/19 is an unrelated drive-by fix.
> > 
> > Patches 16/19 to 18/19 then perform refactoring of the DU driver, to
> > finally add writeback support in patch 19/19.
> > 
> > The writeback pixel format is restricted to RGB, due to the VSP1
> > outputting RGB to the display and lacking a separate colour space
> > conversion unit for writeback. The resolution can be freely picked by
> > will result in cropping or composing, not scaling.
> 
> I think this sentence about cropping doesn't match the implementation,
> as you're checking the framebuffer size against the mode.

That's true, it was different when using V4L2, but now the resolution is
fixed.

> We want to enable scaling/composing on writeback (which our HW can
> do). I thought we'd add WRITEBACK_X/Y/W/H properties to set the
> destination buffer coordinates. One of our team is working on that
> now.
> 
> We don't have cropping, but I thought that could be achieved by
> exposing CRTC_X/Y/W/H on writeback connectors.

I don't have a use case from cropping or composing writeback at the
moment, but such an API would likely work.

> > Writeback requests are queued to the hardware on page flip (atomic
> > flush), and complete at the next vblank. This means that a queued
> > writeback buffer will not be processed until the next page flip, but
> > once it starts being written to by the VSP, it will complete at the next
> > vblank regardless of whether another page flip occurs at that time.
> > 
> > The code is based on a merge of the media master branch, the drm-next
> > branch and the R-Car DT next branch. For convenience patches can be
> > found at
> > 
> >         git://linuxtv.org/pinchartl/media.git v4l2/vsp1/writeback
> > 
> > Kieran Bingham (1):
> >   Revert "[media] v4l: vsp1: Supply frames to the DU continuously"
> > 
> > Laurent Pinchart (18):
> >   media: vsp1: wpf: Fix partition configuration for display pipelines
> >   media: vsp1: Replace leftover occurrence of fragment with body
> >   media: vsp1: Fix addresses of display-related registers for VSP-DL
> >   media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse
> >   media: vsp1: Replace the display list internal flag with a flags field
> >   media: vsp1: dl: Support one-shot entries in the display list
> >   media: vsp1: wpf: Add writeback support
> >   media: vsp1: drm: Split RPF format setting to separate function
> >   media: vsp1: drm: Extend frame completion API to the DU driver
> >   media: vsp1: drm: Implement writeback support
> >   drm: writeback: Cleanup job ownership handling when queuing job
> >   drm: writeback: Fix leak of writeback job
> >   drm: writeback: Add job prepare and cleanup operations
> >   drm/msm: Remove prototypes for non-existing functions
> >   drm: rcar-du: Fix rcar_du_crtc structure documentation
> >   drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure
> >   drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate
> >     functions
> >   drm: rcar-du: Add writeback support for R-Car Gen3
> > 
> >  drivers/gpu/drm/arm/malidp_mw.c             |   3 +-
> >  drivers/gpu/drm/drm_atomic_helper.c         |  11 ++
> >  drivers/gpu/drm/drm_atomic_state_helper.c   |   4 +
> >  drivers/gpu/drm/drm_atomic_uapi.c           |  31 +--
> >  drivers/gpu/drm/drm_writeback.c             |  71 ++++++-
> >  drivers/gpu/drm/msm/msm_drv.h               |   2 -
> >  drivers/gpu/drm/rcar-du/Kconfig             |   4 +
> >  drivers/gpu/drm/rcar-du/Makefile            |   3 +-
> >  drivers/gpu/drm/rcar-du/rcar_du_crtc.h      |   9 +-
> >  drivers/gpu/drm/rcar-du/rcar_du_kms.c       |  37 ++++
> >  drivers/gpu/drm/rcar-du/rcar_du_kms.h       |   1 +
> >  drivers/gpu/drm/rcar-du/rcar_du_vsp.c       | 121 ++++++------
> >  drivers/gpu/drm/rcar-du/rcar_du_vsp.h       |  17 ++
> >  drivers/gpu/drm/rcar-du/rcar_du_writeback.c | 203 ++++++++++++++++++++
> >  drivers/gpu/drm/rcar-du/rcar_du_writeback.h |  39 ++++
> >  drivers/gpu/drm/vc4/vc4_txp.c               |   2 +-
> >  drivers/media/platform/vsp1/vsp1_dl.c       | 127 ++++++++++--
> >  drivers/media/platform/vsp1/vsp1_dl.h       |   8 +-
> >  drivers/media/platform/vsp1/vsp1_drm.c      |  92 ++++++---
> >  drivers/media/platform/vsp1/vsp1_drm.h      |   2 +-
> >  drivers/media/platform/vsp1/vsp1_drv.c      |  15 ++
> >  drivers/media/platform/vsp1/vsp1_pipe.c     |   5 +
> >  drivers/media/platform/vsp1/vsp1_pipe.h     |   1 +
> >  drivers/media/platform/vsp1/vsp1_regs.h     |   6 +-
> >  drivers/media/platform/vsp1/vsp1_rwpf.h     |   2 +
> >  drivers/media/platform/vsp1/vsp1_video.c    |  49 +++--
> >  drivers/media/platform/vsp1/vsp1_wpf.c      |  68 +++++--
> >  include/drm/drm_modeset_helper_vtables.h    |   7 +
> >  include/drm/drm_writeback.h                 |  30 ++-
> >  include/media/vsp1.h                        |  19 +-
> >  30 files changed, 790 insertions(+), 199 deletions(-)
> >  create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.c
> >  create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.h

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-22 13:50       ` Brian Starkey
@ 2019-02-22 14:49         ` Laurent Pinchart
  2019-02-22 15:11           ` Brian Starkey
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-22 14:49 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Fri, Feb 22, 2019 at 01:50:01PM +0000, Brian Starkey wrote:
> On Fri, Feb 22, 2019 at 12:12:00AM +0200, Laurent Pinchart wrote:
> > On Thu, Feb 21, 2019 at 06:12:03PM +0000, Brian Starkey wrote:
> >> On Thu, Feb 21, 2019 at 12:32:07PM +0200, Laurent Pinchart wrote:
> >>> As writeback jobs contain a framebuffer, drivers may need to prepare and
> >>> cleanup them the same way they can prepare and cleanup framebuffers for
> >>> planes. Add two new optional connector helper operations,
> >>> .prepare_writeback_job() and .cleanup_writeback_job() to support this.
> >>> 
> >>> The job prepare operation is called from
> >>> drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
> >>> that would need to be called by all drivers not using
> >>> drm_atomic_helper_commit(). The job cleanup operation is called from the
> >>> existing drm_writeback_cleanup_job() function, invoked both when
> >>> destroying the job as part of a aborted commit, or when the job
> >>> completes.
> >>> 
> >>> The drm_writeback_job structure is extended with a priv field to let
> >>> drivers store per-job data, such as mappings related to the writeback
> >>> framebuffer.
> >>> 
> >>> For internal plumbing reasons the drm_writeback_job structure needs to
> >>> store a back-pointer to the drm_writeback_connector. To avoid pushing
> >>> too much writeback-specific knowledge to drm_atomic_uapi.c, create a
> >>> drm_writeback_set_fb() function, move the writeback job setup code
> >>> there, and set the connector backpointer. The prepare_signaling()
> >>> function doesn't need to allocate writeback jobs and can ignore
> >>> connectors without a job, as it is called after the writeback jobs are
> >>> allocated to store framebuffers, and a writeback fence with a
> >>> framebuffer is an invalid configuration that gets rejected by the commit
> >>> check.
> >>> 
> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >> 
> >> I probably need to revisit this with fresh eyes tomorrow, but how come
> >> the prepared-ness and whatever is being prepared can't be put into a
> >> subclassed framebuffer? That seems more natural to me, than tracking
> >> it with the job. It's really the framebuffer we're preparing after
> >> all.
> > 
> > In my patch series I indeed need to track information related to the
> > framebuffer only, but is it a good idea to limit writeback
> > prepare/cleanup to that ? Other drivers may have different needs.
> 
> IMO better to write the code for the case we know, rather than
> speculate.
> 
> > Furthermore, the mapping needs to exist for the lifetime of the
> > writeback job, so I'd have to add a counter to the framebuffer subclass
> > to track these too. While doable, it starts sounding a bit like a hack,
> > and may create race conditions.
> 
> You're probably right. As you were saying to Daniel - writeback things
> don't have the same lifetime as the state, so the job is the only
> place to store things which would normally go in the state.
> 
> >> I guess if you have the same framebuffer in use for multiple things,
> >> you might need to track it separately? Can the mappings be shared in
> >> that case?
> > 
> > Speaking about my case, the mapping is per-CRTC, so if I use the same
> > framebuffer for multiple CRTCs I'd need separate mappings. Other drivers
> > may have simpler or more complex needs.
> > 
> > When you'll revisit this with fresh eyes, I'd like to know if you think
> > it would be worth it replacing the priv pointer with allocate/destroy
> > operations to allow subclassing the writeback job. We could possibly do
> > without the destroy operation if we mandate embedding the writeback job
> > as the first field of the subclass and usage of kmalloc (& friends) to
> > allocate the subclass structure. We could even do without the allocate
> > operation if we just stored the size of the subclass in the writeback
> > connector itself, but that would depart from what we usually do in DRM.
> 
> I think enabling the job to be subclassed would be a fine solution,
> with functions which are as close as possible to the other DRM
> objects.
> 
> I'm not sure there's a lot of advantage in skipping allocate/destroy;
> it should be fine to just have helpers for the common case.
> 
> I wonder if renaming it to "writeback_state" instead of
> "writeback_job" is too confusing (because it doesn't _quite_ act like
> the other states, even though its purpose is very similar).

I'd keep job, as it works a bit differently from states.

> > I would also like to know if I should create a writeback connector
> > operations structure to store the prepare/cleanup and perhaps
> > allocate/destroy operations instead of adding them to the connector
> > helper funcs.
> 
> I think just adding the functions to the existing connector
> operations is overall less clutter, but I don't mind either way.

That would be the connector helper functions, not the connector
functions, right ?

Daniel, do you have a preference ?

> >>> ---
> >>>  drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
> >>>  drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
> >>>  drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
> >>>  include/drm/drm_modeset_helper_vtables.h |  7 ++++
> >>>  include/drm/drm_writeback.h              | 28 ++++++++++++++-
> >>>  5 files changed, 96 insertions(+), 24 deletions(-)
> >>> 
> >>> This patch is currently missing documentation for the
> >>> .prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
> >>> to fix this, but first wanted feedback on the direction taken. I'm not
> >>> entirely happy with the priv pointer in the drm_writeback_job structure,
> >>> but adding a full state duplicate/destroy machinery for that structure
> >>> was equally unappealing to me.
> >>> 
> >>> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> >>> index 6fe2303fccd9..70a4886c6e65 100644
> >>> --- a/drivers/gpu/drm/drm_atomic_helper.c
> >>> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> >>> @@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
> >>>  int drm_atomic_helper_prepare_planes(struct drm_device *dev,
> >>>  				     struct drm_atomic_state *state)
> >>>  {
> >>> +	struct drm_connector *connector;
> >>> +	struct drm_connector_state *new_conn_state;
> >>>  	struct drm_plane *plane;
> >>>  	struct drm_plane_state *new_plane_state;
> >>>  	int ret, i, j;
> >>>  
> >>> +	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
> >>> +		if (!new_conn_state->writeback_job)
> >>> +			continue;
> >>> +
> >>> +		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
> >>> +		if (ret < 0)
> >>> +			return ret;
> >>> +	}
> >>> +
> >>>  	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
> >>>  		const struct drm_plane_helper_funcs *funcs;
> >>>  
> >>> diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
> >>> index c40889888a16..e802152a01ad 100644
> >>> --- a/drivers/gpu/drm/drm_atomic_uapi.c
> >>> +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> >>> @@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> >>>  	return 0;
> >>>  }
> >>>  
> >>> -static struct drm_writeback_job *
> >>> -drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
> >>> -{
> >>> -	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> >>> -
> >>> -	if (!conn_state->writeback_job)
> >>> -		conn_state->writeback_job =
> >>> -			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> >>> -
> >>> -	return conn_state->writeback_job;
> >>> -}
> >>> -
> >>>  static int drm_atomic_set_writeback_fb_for_connector(
> >>>  		struct drm_connector_state *conn_state,
> >>>  		struct drm_framebuffer *fb)
> >>>  {
> >>> -	struct drm_writeback_job *job =
> >>> -		drm_atomic_get_writeback_job(conn_state);
> >>> -	if (!job)
> >>> -		return -ENOMEM;
> >>> +	int ret;
> >>>  
> >>> -	drm_framebuffer_assign(&job->fb, fb);
> >>> +	ret = drm_writeback_set_fb(conn_state, fb);
> >>> +	if (ret < 0)
> >>> +		return ret;
> >>>  
> >>>  	if (fb)
> >>>  		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
> >>> @@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
> >>>  
> >>>  	for_each_new_connector_in_state(state, conn, conn_state, i) {
> >>>  		struct drm_writeback_connector *wb_conn;
> >>> -		struct drm_writeback_job *job;
> >>>  		struct drm_out_fence_state *f;
> >>>  		struct dma_fence *fence;
> >>>  		s32 __user *fence_ptr;
> >>>  
> >>> +		if (!conn_state->writeback_job)
> >>> +			continue;
> >>> +
> >>>  		fence_ptr = get_out_fence_for_connector(state, conn);
> >>>  		if (!fence_ptr)
> >>>  			continue;
> >>>  
> >>> -		job = drm_atomic_get_writeback_job(conn_state);
> >>> -		if (!job)
> >>> -			return -ENOMEM;
> >>> -
> >>>  		f = krealloc(*fence_state, sizeof(**fence_state) *
> >>>  			     (*num_fences + 1), GFP_KERNEL);
> >>>  		if (!f)
> >>> @@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
> >>>  			return ret;
> >>>  		}
> >>>  
> >>> -		job->out_fence = fence;
> >>> +		conn_state->writeback_job->out_fence = fence;
> >>>  	}
> >>>  
> >>>  	/*
> >>> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> >>> index afb1ae6e0ecb..4678d61d634a 100644
> >>> --- a/drivers/gpu/drm/drm_writeback.c
> >>> +++ b/drivers/gpu/drm/drm_writeback.c
> >>> @@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >>>  }
> >>>  EXPORT_SYMBOL(drm_writeback_connector_init);
> >>>  
> >>> +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> >>> +			 struct drm_framebuffer *fb)
> >>> +{
> >>> +	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> >>> +
> >>> +	if (!conn_state->writeback_job) {
> >>> +		conn_state->writeback_job =
> >>> +			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> >>> +		if (!conn_state->writeback_job)
> >>> +			return -ENOMEM;
> >>> +
> >>> +		conn_state->writeback_job->connector =
> >>> +			drm_connector_to_writeback(conn_state->connector);
> >>> +	}
> >>> +
> >>> +	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +int drm_writeback_prepare_job(struct drm_writeback_job *job)
> >>> +{
> >>> +	struct drm_writeback_connector *connector = job->connector;
> >>> +	const struct drm_connector_helper_funcs *funcs =
> >>> +		connector->base.helper_private;
> >>> +	int ret;
> >>> +
> >>> +	if (funcs->cleanup_writeback_job) {
> >>> +		ret = funcs->prepare_writeback_job(connector, job);
> >>> +		if (ret < 0)
> >>> +			return ret;
> >>> +	}
> >>> +
> >>> +	job->prepared = true;
> >>> +	return 0;
> >>> +}
> >>> +
> >>>  /**
> >>>   * drm_writeback_queue_job - Queue a writeback job for later signalling
> >>>   * @wb_connector: The writeback connector to queue a job on
> >>> @@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
> >>>  
> >>>  void drm_writeback_cleanup_job(struct drm_writeback_job *job)
> >>>  {
> >>> +	struct drm_writeback_connector *connector = job->connector;
> >>> +	const struct drm_connector_helper_funcs *funcs =
> >>> +		connector->base.helper_private;
> >>> +
> >>> +	if (job->prepared && funcs->cleanup_writeback_job)
> >>> +		funcs->cleanup_writeback_job(connector, job);
> >>> +
> >>>  	if (job->fb)
> >>>  		drm_framebuffer_put(job->fb);
> >>>  
> >>> diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> >>> index 61142aa0ab23..73d03fe66799 100644
> >>> --- a/include/drm/drm_modeset_helper_vtables.h
> >>> +++ b/include/drm/drm_modeset_helper_vtables.h
> >>> @@ -49,6 +49,8 @@
> >>>   */
> >>>  
> >>>  enum mode_set_atomic;
> >>> +struct drm_writeback_connector;
> >>> +struct drm_writeback_job;
> >>>  
> >>>  /**
> >>>   * struct drm_crtc_helper_funcs - helper operations for CRTCs
> >>> @@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
> >>>  	 */
> >>>  	void (*atomic_commit)(struct drm_connector *connector,
> >>>  			      struct drm_connector_state *state);
> >>> +
> >>> +	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
> >>> +				     struct drm_writeback_job *job);
> >>> +	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
> >>> +				      struct drm_writeback_job *job);
> >>>  };
> >>>  
> >>>  /**
> >>> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> >>> index 47662c362743..777c14c847f0 100644
> >>> --- a/include/drm/drm_writeback.h
> >>> +++ b/include/drm/drm_writeback.h
> >>> @@ -79,6 +79,20 @@ struct drm_writeback_connector {
> >>>  };
> >>>  
> >>>  struct drm_writeback_job {
> >>> +	/**
> >>> +	 * @connector:
> >>> +	 *
> >>> +	 * Back-pointer to the writeback connector associated with the job
> >>> +	 */
> >>> +	struct drm_writeback_connector *connector;
> >> 
> >> It kind-of feels like this shouldn't be necessary, but I think
> >> avoiding it would mean either allocating the work_struct outside of
> >> the job, and tracking the connector there instead of in the job, or
> >> having two calls to unprepare - one in signal_completion and one in
> >> destroy_state.
> > 
> > I don't like the second option as unprepare could potentially be costly,
> > and should thus not be called from signal_completion that may run in
> > interrupt context. The first option is doable, but I think it will
> > result in even worse code.
> > 
> >> It's probably not worth the gymnastics to avoid the backpointer... but
> >> for some reason the backpointer feels intangibly dirty to me.
> > 
> > A writeback job exists in the context of a writeback connector, why do
> > you feel the backpointer is dirty ?
> > 
> >>> +
> >>> +	/**
> >>> +	 * @prepared:
> >>> +	 *
> >>> +	 * Set when the job has been prepared with drm_writeback_prepare_job()
> >>> +	 */
> >>> +	bool prepared;
> >>> +
> >>>  	/**
> >>>  	 * @cleanup_work:
> >>>  	 *
> >>> @@ -98,7 +112,7 @@ struct drm_writeback_job {
> >>>  	 * @fb:
> >>>  	 *
> >>>  	 * Framebuffer to be written to by the writeback connector. Do not set
> >>> -	 * directly, use drm_atomic_set_writeback_fb_for_connector()
> >>> +	 * directly, use drm_writeback_set_fb()
> >>>  	 */
> >>>  	struct drm_framebuffer *fb;
> >>>  
> >>> @@ -108,6 +122,13 @@ struct drm_writeback_job {
> >>>  	 * Fence which will signal once the writeback has completed
> >>>  	 */
> >>>  	struct dma_fence *out_fence;
> >>> +
> >>> +	/**
> >>> +	 * @priv:
> >>> +	 *
> >>> +	 * Driver-private data
> >>> +	 */
> >>> +	void *priv;
> >>>  };
> >>>  
> >>>  static inline struct drm_writeback_connector *
> >>> @@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >>>  				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> >>>  				 const u32 *formats, int n_formats);
> >>>  
> >>> +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> >>> +			 struct drm_framebuffer *fb);
> >>> +
> >>> +int drm_writeback_prepare_job(struct drm_writeback_job *job);
> >>> +
> >>>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> >>>  			     struct drm_connector_state *conn_state);
> >>>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-02-22 14:46     ` Laurent Pinchart
@ 2019-02-22 15:06       ` Brian Starkey
  2019-03-05 23:14         ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-02-22 15:06 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> Hi Brian,
> 
> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> > On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > > One-shot entries are used as an alternative to committing a complete new
> > > display list when a couple of registers need to be written for one frame
> > > and then reset to another value for all subsequent frames. This will be
> > > used to implement writeback support that will need to enable writeback
> > > for the duration of a single frame.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > > ---
> > >  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> > >  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> > >  2 files changed, 81 insertions(+)
> > > 
> > > diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > > index 886b3a69d329..7b4d252bfde7 100644
> > > --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > > +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > > @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> > >  
> > >  	unsigned int num_entries;
> > >  	unsigned int max_entries;
> > > +
> > > +	unsigned int num_patches;
> > > +	struct {
> > > +		struct vsp1_dl_entry *entry;
> > > +		u32 data;
> > > +	} patches[2];
> > >  };
> > >  
> > >  /**
> > > @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> > >  		return;
> > >  
> > >  	dlb->num_entries = 0;
> > > +	dlb->num_patches = 0;
> > >  
> > >  	spin_lock_irqsave(&dlb->pool->lock, flags);
> > >  	list_add_tail(&dlb->free, &dlb->pool->free);
> > > @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> > >  	dlb->num_entries++;
> > >  }
> > >  
> > > +/**
> > > + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > > + *	single frame
> > > + * @dlb: The body
> > > + * @reg: The register address
> > > + * @value: The register value
> > > + * @reset_value: The value to reset the register to at the next vblank
> > > + *
> > > + * Display lists in continuous mode are re-used by the hardware for successive
> > > + * frames until a new display list is committed. Changing the VSP configuration
> > > + * normally requires creating and committing a new display list. This function
> > > + * offers an alternative race-free way by writing a @value to the @register in
> > > + * the display list body for a single frame, specifying in @reset_value the
> > > + * value to reset the register to one vblank after the display list is
> > > + * committed.
> > > + *
> > > + * The maximum number of one-shot entries is limited to 2 per display list body,
> > > + * and one-shot entries are counted in the total number of entries specified
> > > + * when the body is allocated by vsp1_dl_body_alloc().
> > > + */
> > > +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > > +				u32 reset_value)
> > > +{
> > > +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > > +		      "DLB size exceeded (max %u)", dlb->max_entries))
> > > +		return;
> > > +
> > > +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > > +		      "DLB patches size exceeded (max %zu)",
> > > +		      ARRAY_SIZE(dlb->patches)))
> > > +		return;
> > > +
> > > +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > > +	dlb->patches[dlb->num_patches].data = reset_value;
> > > +	dlb->num_patches++;
> > > +
> > > +	dlb->entries[dlb->num_entries].addr = reg;
> > > +	dlb->entries[dlb->num_entries].data = value;
> > > +	dlb->num_entries++;
> > > +}
> > > +
> > >  /* -----------------------------------------------------------------------------
> > >   * Display List Extended Command Management
> > >   */
> > > @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> > >  	 * has at least one body, thus we reinitialise the entries list.
> > >  	 */
> > >  	dl->body0->num_entries = 0;
> > > +	dl->body0->num_patches = 0;
> > >  
> > >  	list_add_tail(&dl->list, &dl->dlm->free);
> > >  }
> > > @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> > >   * Display List Manager
> > >   */
> > >  
> > > +/**
> > > + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > > + *	interrupt
> > > + * @dlm: the display list manager
> > > + *
> > > + * Apply all one-shot patches registered for the active display list.
> > > + */
> > > +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > > +{
> > > +	struct vsp1_dl_body *dlb;
> > > +	struct vsp1_dl_list *dl;
> > > +	unsigned int i;
> > > +
> > > +	spin_lock(&dlm->lock);
> > > +
> > > +	dl = dlm->active;
> > > +	if (!dl)
> > > +		goto done;
> > > +
> > > +	list_for_each_entry(dlb, &dl->bodies, list) {
> > > +		for (i = 0; i < dlb->num_patches; ++i)
> > > +			dlb->patches[i].entry->data = dlb->patches[i].data;
> > > +		dlb->num_patches = 0;
> > > +	}
> > > +
> > > +done:
> > > +	spin_unlock(&dlm->lock);
> > > +}
> > > +
> > 
> > We've got some HW which doesn't support one-shot writeback, and use a
> > similar trick to try and disable writeback immediately after the flip.
> > 
> > We ran into issues where the "start" interrupt wouldn't run in time to
> > make sure the writeback disable was committed before the next frame.
> > We have to keep track of whether the disable really happened in time,
> > before we release the output buffer.
> > 
> > Might you have a similar problem here?
> 
> We may, but there's no provision at the hardware level to check if the
> configuration updated happened in time. I could add some safety checks
> but I believe they would be racy in the best case :-(

We managed to find (what I believe to be...) a non-racy way, but it
will of course depend a lot on the HW behaviour, so I'll leave it to
your best judgement.

We basically have a "configuration committed" interrupt which we can
use to set a flag indicating writeback was disabled.

> 
> Note that we have the duration of a complete frame to disable writeback,
> as we receive an interrupt when the frame starts, and have until vblank
> to update the configuration. It's thus slightly better than having to
> disable writeback between vblank and the start of the next frame.

Yeah... we have a whole frame too. I'm struggling to find our wiki
page with the data, but anecdotally there's some (out-of-tree) drivers
which keep interrupts masked for a _really long_ time. It's nice if
you don't have to care about those :-)

-Brian

> 
> > >  /**
> > >   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> > >   * @dlm: the display list manager
> > > diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > > index e0fdb145e6ed..f845607abc4c 100644
> > > --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > > +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > > @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> > >  					unsigned int prealloc);
> > >  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> > >  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > > +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> > >  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> > >  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> > >  
> > > @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> > >  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> > >  
> > >  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > > +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > > +				u32 reset_value);
> > >  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> > >  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> > >  
> 
> -- 
> Regards,
> 
> Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-22 14:49         ` Laurent Pinchart
@ 2019-02-22 15:11           ` Brian Starkey
  0 siblings, 0 replies; 65+ messages in thread
From: Brian Starkey @ 2019-02-22 15:11 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Fri, Feb 22, 2019 at 04:49:53PM +0200, Laurent Pinchart wrote:
> 
> That would be the connector helper functions, not the connector
> functions, right ?

Yes, sorry.

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job
  2019-02-21 10:32 ` [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job Laurent Pinchart
  2019-02-21 10:42   ` Laurent Pinchart
  2019-02-21 16:40   ` Eric Anholt
@ 2019-02-26 18:07   ` Liviu Dudau
  2 siblings, 0 replies; 65+ messages in thread
From: Liviu Dudau @ 2019-02-26 18:07 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: James Qian Wang, dri-devel, Kieran Bingham

On Thu, Feb 21, 2019 at 12:32:05PM +0200, Laurent Pinchart wrote:
> The drm_writeback_queue_job() function takes ownership of the passed job
> and requires the caller to manually set the connector state
> writeback_job pointer to NULL. To simplify drivers and avoid errors
> (such as the missing NULL set in the vc4 driver), pass the connector
> state pointer to the function instead of the job pointer, and set the
> writeback_job pointer to NULL internally.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Acked-by: Liviu Dudau <liviu.dudau@arm.com>

Best regards,
Liviu

> ---
>  drivers/gpu/drm/arm/malidp_mw.c |  3 +--
>  drivers/gpu/drm/drm_writeback.c | 15 ++++++++++-----
>  drivers/gpu/drm/vc4/vc4_txp.c   |  2 +-
>  include/drm/drm_writeback.h     |  2 +-
>  4 files changed, 13 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
> index 041a64dc7167..87627219ce3b 100644
> --- a/drivers/gpu/drm/arm/malidp_mw.c
> +++ b/drivers/gpu/drm/arm/malidp_mw.c
> @@ -252,8 +252,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm,
>  				     &mw_state->addrs[0],
>  				     mw_state->format);
>  
> -		drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
> -		conn_state->writeback_job = NULL;
> +		drm_writeback_queue_job(mw_conn, conn_state);
>  		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
>  					   mw_state->pitches, mw_state->n_planes,
>  					   fb->width, fb->height, mw_state->format,
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index c20e6fe00cb3..338b993d7c9f 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -242,11 +242,12 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
>  /**
>   * drm_writeback_queue_job - Queue a writeback job for later signalling
>   * @wb_connector: The writeback connector to queue a job on
> - * @job: The job to queue
> + * @conn_state: The connector state containing the job to queue
>   *
> - * This function adds a job to the job_queue for a writeback connector. It
> - * should be considered to take ownership of the writeback job, and so any other
> - * references to the job must be cleared after calling this function.
> + * This function adds the job contained in @conn_state to the job_queue for a
> + * writeback connector. It takes ownership of the writeback job and sets the
> + * @conn_state->writeback_job to NULL, and so no access to the job may be
> + * performed by the caller after this function returns.
>   *
>   * Drivers must ensure that for a given writeback connector, jobs are queued in
>   * exactly the same order as they will be completed by the hardware (and
> @@ -258,10 +259,14 @@ EXPORT_SYMBOL(drm_writeback_connector_init);
>   * See also: drm_writeback_signal_completion()
>   */
>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> -			     struct drm_writeback_job *job)
> +			     struct drm_connector_state *conn_state)
>  {
> +	struct drm_writeback_job *job;
>  	unsigned long flags;
>  
> +	job = conn_state->writeback_job;
> +	conn_state->writeback_job = NULL;
> +
>  	spin_lock_irqsave(&wb_connector->job_lock, flags);
>  	list_add_tail(&job->list_entry, &wb_connector->job_queue);
>  	spin_unlock_irqrestore(&wb_connector->job_lock, flags);
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index aa279b5b0de7..5dabd91f2d7e 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -327,7 +327,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
>  
>  	TXP_WRITE(TXP_DST_CTRL, ctrl);
>  
> -	drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
> +	drm_writeback_queue_job(&txp->connector, conn_state);
>  }
>  
>  static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> index 23df9d463003..47662c362743 100644
> --- a/include/drm/drm_writeback.h
> +++ b/include/drm/drm_writeback.h
> @@ -123,7 +123,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
>  				 const u32 *formats, int n_formats);
>  
>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> -			     struct drm_writeback_job *job);
> +			     struct drm_connector_state *conn_state);
>  
>  void drm_writeback_cleanup_job(struct drm_writeback_job *job);
>  
> -- 
> Regards,
> 
> Laurent Pinchart
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 13/19] drm: writeback: Fix leak of writeback job
  2019-02-21 10:32 ` [PATCH v5 13/19] drm: writeback: Fix leak of writeback job Laurent Pinchart
  2019-02-21 17:48   ` Brian Starkey
@ 2019-02-26 18:10   ` Liviu Dudau
  1 sibling, 0 replies; 65+ messages in thread
From: Liviu Dudau @ 2019-02-26 18:10 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: James Qian Wang, dri-devel, Kieran Bingham

On Thu, Feb 21, 2019 at 12:32:06PM +0200, Laurent Pinchart wrote:
> Writeback jobs are allocated when the WRITEBACK_FB_ID is set, and
> deleted when the jobs complete. This results in both a memory leak of
> the job and a leak of the framebuffer if the atomic commit returns
> before the job is queued for processing, for instance if the atomic
> check fails or if the commit runs in test-only mode.
> 
> Fix this by implementing the drm_writeback_cleanup_job() function and
> calling it from __drm_atomic_helper_connector_destroy_state(). As
> writeback jobs are removed from the state when they're queued for
> processing, any job left in the state when the state gets destroyed
> needs to be cleaned up.
> 
> The existing declaration of the drm_writeback_cleanup_job() function
> without an implementation hints that this problem was considered, but
> never addressed.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Acked-by: Liviu Dudau <liviu.dudau@arm.com>

Best regards,
Liviu

> ---
>  drivers/gpu/drm/drm_atomic_state_helper.c |  4 ++++
>  drivers/gpu/drm/drm_writeback.c           | 13 ++++++++++---
>  2 files changed, 14 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 4985384e51f6..59ffb6b9c745 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -30,6 +30,7 @@
>  #include <drm/drm_connector.h>
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_device.h>
> +#include <drm/drm_writeback.h>
>  
>  #include <linux/slab.h>
>  #include <linux/dma-fence.h>
> @@ -412,6 +413,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
>  
>  	if (state->commit)
>  		drm_crtc_commit_put(state->commit);
> +
> +	if (state->writeback_job)
> +		drm_writeback_cleanup_job(state->writeback_job);
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
>  
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index 338b993d7c9f..afb1ae6e0ecb 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -273,6 +273,14 @@ void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
>  }
>  EXPORT_SYMBOL(drm_writeback_queue_job);
>  
> +void drm_writeback_cleanup_job(struct drm_writeback_job *job)
> +{
> +	if (job->fb)
> +		drm_framebuffer_put(job->fb);
> +
> +	kfree(job);
> +}
> +
>  /*
>   * @cleanup_work: deferred cleanup of a writeback job
>   *
> @@ -285,10 +293,9 @@ static void cleanup_work(struct work_struct *work)
>  	struct drm_writeback_job *job = container_of(work,
>  						     struct drm_writeback_job,
>  						     cleanup_work);
> -	drm_framebuffer_put(job->fb);
> -	kfree(job);
> -}
>  
> +	drm_writeback_cleanup_job(job);
> +}
>  
>  /**
>   * drm_writeback_signal_completion - Signal the completion of a writeback job
> -- 
> Regards,
> 
> Laurent Pinchart
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-21 10:32 ` [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations Laurent Pinchart
  2019-02-21 18:12   ` Brian Starkey
@ 2019-02-26 18:39   ` Liviu Dudau
  2019-02-27 12:38     ` Laurent Pinchart
  1 sibling, 1 reply; 65+ messages in thread
From: Liviu Dudau @ 2019-02-26 18:39 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: James Qian Wang, dri-devel, Kieran Bingham

Hi Laurent,

On Thu, Feb 21, 2019 at 12:32:07PM +0200, Laurent Pinchart wrote:
> As writeback jobs contain a framebuffer, drivers may need to prepare and
> cleanup them the same way they can prepare and cleanup framebuffers for
> planes. Add two new optional connector helper operations,
> .prepare_writeback_job() and .cleanup_writeback_job() to support this.

I'm having a bit of a hard time parsing the above paragraph. I think that
what you are saying is that you need to prepare and cleanup the framebuffers that
writeback jobs have, but it also can be read that you need to prepare/cleanup
the actual jobs. If the latter, then I'm curious to know what is special
about the jobs that you need preparing/cleaning up.

> 
> The job prepare operation is called from
> drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
> that would need to be called by all drivers not using
> drm_atomic_helper_commit(). The job cleanup operation is called from the
> existing drm_writeback_cleanup_job() function, invoked both when
> destroying the job as part of a aborted commit, or when the job
> completes.
> 
> The drm_writeback_job structure is extended with a priv field to let
> drivers store per-job data, such as mappings related to the writeback
> framebuffer.

Could the driver store in this priv field a structure that contains the
connector, whereby removing the need for a back-pointer?

> 
> For internal plumbing reasons the drm_writeback_job structure needs to
> store a back-pointer to the drm_writeback_connector. To avoid pushing
> too much writeback-specific knowledge to drm_atomic_uapi.c, create a
> drm_writeback_set_fb() function, move the writeback job setup code
> there, and set the connector backpointer. The prepare_signaling()
> function doesn't need to allocate writeback jobs and can ignore
> connectors without a job, as it is called after the writeback jobs are
> allocated to store framebuffers, and a writeback fence with a
> framebuffer is an invalid configuration that gets rejected by the commit
> check.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
>  drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
>  drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
>  include/drm/drm_modeset_helper_vtables.h |  7 ++++
>  include/drm/drm_writeback.h              | 28 ++++++++++++++-
>  5 files changed, 96 insertions(+), 24 deletions(-)
> 
> This patch is currently missing documentation for the
> .prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
> to fix this, but first wanted feedback on the direction taken. I'm not
> entirely happy with the priv pointer in the drm_writeback_job structure,
> but adding a full state duplicate/destroy machinery for that structure
> was equally unappealing to me.
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 6fe2303fccd9..70a4886c6e65 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
>  int drm_atomic_helper_prepare_planes(struct drm_device *dev,
>  				     struct drm_atomic_state *state)
>  {
> +	struct drm_connector *connector;
> +	struct drm_connector_state *new_conn_state;
>  	struct drm_plane *plane;
>  	struct drm_plane_state *new_plane_state;
>  	int ret, i, j;
>  
> +	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
> +		if (!new_conn_state->writeback_job)
> +			continue;
> +
> +		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
>  	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
>  		const struct drm_plane_helper_funcs *funcs;
>  
> diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
> index c40889888a16..e802152a01ad 100644
> --- a/drivers/gpu/drm/drm_atomic_uapi.c
> +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> @@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
>  	return 0;
>  }
>  
> -static struct drm_writeback_job *
> -drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
> -{
> -	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> -
> -	if (!conn_state->writeback_job)
> -		conn_state->writeback_job =
> -			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> -
> -	return conn_state->writeback_job;
> -}
> -
>  static int drm_atomic_set_writeback_fb_for_connector(
>  		struct drm_connector_state *conn_state,
>  		struct drm_framebuffer *fb)
>  {
> -	struct drm_writeback_job *job =
> -		drm_atomic_get_writeback_job(conn_state);
> -	if (!job)
> -		return -ENOMEM;
> +	int ret;
>  
> -	drm_framebuffer_assign(&job->fb, fb);
> +	ret = drm_writeback_set_fb(conn_state, fb);
> +	if (ret < 0)
> +		return ret;
>  
>  	if (fb)
>  		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
> @@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
>  
>  	for_each_new_connector_in_state(state, conn, conn_state, i) {
>  		struct drm_writeback_connector *wb_conn;
> -		struct drm_writeback_job *job;
>  		struct drm_out_fence_state *f;
>  		struct dma_fence *fence;
>  		s32 __user *fence_ptr;
>  
> +		if (!conn_state->writeback_job)
> +			continue;
> +
>  		fence_ptr = get_out_fence_for_connector(state, conn);
>  		if (!fence_ptr)
>  			continue;
>  
> -		job = drm_atomic_get_writeback_job(conn_state);
> -		if (!job)
> -			return -ENOMEM;
> -
>  		f = krealloc(*fence_state, sizeof(**fence_state) *
>  			     (*num_fences + 1), GFP_KERNEL);
>  		if (!f)
> @@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
>  			return ret;
>  		}
>  
> -		job->out_fence = fence;
> +		conn_state->writeback_job->out_fence = fence;
>  	}
>  
>  	/*
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index afb1ae6e0ecb..4678d61d634a 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
>  }
>  EXPORT_SYMBOL(drm_writeback_connector_init);
>  
> +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> +			 struct drm_framebuffer *fb)
> +{
> +	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> +
> +	if (!conn_state->writeback_job) {
> +		conn_state->writeback_job =
> +			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> +		if (!conn_state->writeback_job)
> +			return -ENOMEM;
> +
> +		conn_state->writeback_job->connector =
> +			drm_connector_to_writeback(conn_state->connector);
> +	}
> +
> +	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
> +	return 0;
> +}
> +
> +int drm_writeback_prepare_job(struct drm_writeback_job *job)
> +{
> +	struct drm_writeback_connector *connector = job->connector;
> +	const struct drm_connector_helper_funcs *funcs =
> +		connector->base.helper_private;
> +	int ret;
> +
> +	if (funcs->cleanup_writeback_job) {

This feels weird, did you mean to actually check that the funcs->prepare_writeback_job
hook is implemented?

Otherwise, things look good to me!

Best regards,
Liviu

> +		ret = funcs->prepare_writeback_job(connector, job);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	job->prepared = true;
> +	return 0;
> +}
> +
>  /**
>   * drm_writeback_queue_job - Queue a writeback job for later signalling
>   * @wb_connector: The writeback connector to queue a job on
> @@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
>  
>  void drm_writeback_cleanup_job(struct drm_writeback_job *job)
>  {
> +	struct drm_writeback_connector *connector = job->connector;
> +	const struct drm_connector_helper_funcs *funcs =
> +		connector->base.helper_private;
> +
> +	if (job->prepared && funcs->cleanup_writeback_job)
> +		funcs->cleanup_writeback_job(connector, job);
> +
>  	if (job->fb)
>  		drm_framebuffer_put(job->fb);
>  
> diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> index 61142aa0ab23..73d03fe66799 100644
> --- a/include/drm/drm_modeset_helper_vtables.h
> +++ b/include/drm/drm_modeset_helper_vtables.h
> @@ -49,6 +49,8 @@
>   */
>  
>  enum mode_set_atomic;
> +struct drm_writeback_connector;
> +struct drm_writeback_job;
>  
>  /**
>   * struct drm_crtc_helper_funcs - helper operations for CRTCs
> @@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
>  	 */
>  	void (*atomic_commit)(struct drm_connector *connector,
>  			      struct drm_connector_state *state);
> +
> +	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
> +				     struct drm_writeback_job *job);
> +	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
> +				      struct drm_writeback_job *job);
>  };
>  
>  /**
> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> index 47662c362743..777c14c847f0 100644
> --- a/include/drm/drm_writeback.h
> +++ b/include/drm/drm_writeback.h
> @@ -79,6 +79,20 @@ struct drm_writeback_connector {
>  };
>  
>  struct drm_writeback_job {
> +	/**
> +	 * @connector:
> +	 *
> +	 * Back-pointer to the writeback connector associated with the job
> +	 */
> +	struct drm_writeback_connector *connector;
> +
> +	/**
> +	 * @prepared:
> +	 *
> +	 * Set when the job has been prepared with drm_writeback_prepare_job()
> +	 */
> +	bool prepared;
> +
>  	/**
>  	 * @cleanup_work:
>  	 *
> @@ -98,7 +112,7 @@ struct drm_writeback_job {
>  	 * @fb:
>  	 *
>  	 * Framebuffer to be written to by the writeback connector. Do not set
> -	 * directly, use drm_atomic_set_writeback_fb_for_connector()
> +	 * directly, use drm_writeback_set_fb()
>  	 */
>  	struct drm_framebuffer *fb;
>  
> @@ -108,6 +122,13 @@ struct drm_writeback_job {
>  	 * Fence which will signal once the writeback has completed
>  	 */
>  	struct dma_fence *out_fence;
> +
> +	/**
> +	 * @priv:
> +	 *
> +	 * Driver-private data
> +	 */
> +	void *priv;
>  };
>  
>  static inline struct drm_writeback_connector *
> @@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
>  				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
>  				 const u32 *formats, int n_formats);
>  
> +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> +			 struct drm_framebuffer *fb);
> +
> +int drm_writeback_prepare_job(struct drm_writeback_job *job);
> +
>  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
>  			     struct drm_connector_state *conn_state);
>  
> -- 
> Regards,
> 
> Laurent Pinchart
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations
  2019-02-26 18:39   ` Liviu Dudau
@ 2019-02-27 12:38     ` Laurent Pinchart
  0 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-02-27 12:38 UTC (permalink / raw)
  To: Liviu Dudau; +Cc: Laurent Pinchart, James Qian Wang, dri-devel, Kieran Bingham

Hi Liviu,

On Tue, Feb 26, 2019 at 06:39:40PM +0000, Liviu Dudau wrote:
> On Thu, Feb 21, 2019 at 12:32:07PM +0200, Laurent Pinchart wrote:
> > As writeback jobs contain a framebuffer, drivers may need to prepare and
> > cleanup them the same way they can prepare and cleanup framebuffers for
> > planes. Add two new optional connector helper operations,
> > .prepare_writeback_job() and .cleanup_writeback_job() to support this.
> 
> I'm having a bit of a hard time parsing the above paragraph. I think that
> what you are saying is that you need to prepare and cleanup the framebuffers that
> writeback jobs have, but it also can be read that you need to prepare/cleanup
> the actual jobs. If the latter, then I'm curious to know what is special
> about the jobs that you need preparing/cleaning up.

I meant the framebuffers, but I wouldn't be surprised if the jobs were
later extended with more fields that would require similar preparation.
That's why I think prepare/cleanup job operations make sense.

> > The job prepare operation is called from
> > drm_atomic_helper_prepare_planes() to avoid a new atomic commit helper
> > that would need to be called by all drivers not using
> > drm_atomic_helper_commit(). The job cleanup operation is called from the
> > existing drm_writeback_cleanup_job() function, invoked both when
> > destroying the job as part of a aborted commit, or when the job
> > completes.
> > 
> > The drm_writeback_job structure is extended with a priv field to let
> > drivers store per-job data, such as mappings related to the writeback
> > framebuffer.
> 
> Could the driver store in this priv field a structure that contains the
> connector, whereby removing the need for a back-pointer?

The priv field points to an opaque structure, forcing drivers to use a
standard structure there would make the API more complex in my opinion,
without any added value I can see.

> > For internal plumbing reasons the drm_writeback_job structure needs to
> > store a back-pointer to the drm_writeback_connector. To avoid pushing
> > too much writeback-specific knowledge to drm_atomic_uapi.c, create a
> > drm_writeback_set_fb() function, move the writeback job setup code
> > there, and set the connector backpointer. The prepare_signaling()
> > function doesn't need to allocate writeback jobs and can ignore
> > connectors without a job, as it is called after the writeback jobs are
> > allocated to store framebuffers, and a writeback fence with a
> > framebuffer is an invalid configuration that gets rejected by the commit
> > check.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> >  drivers/gpu/drm/drm_atomic_helper.c      | 11 ++++++
> >  drivers/gpu/drm/drm_atomic_uapi.c        | 31 +++++------------
> >  drivers/gpu/drm/drm_writeback.c          | 43 ++++++++++++++++++++++++
> >  include/drm/drm_modeset_helper_vtables.h |  7 ++++
> >  include/drm/drm_writeback.h              | 28 ++++++++++++++-
> >  5 files changed, 96 insertions(+), 24 deletions(-)
> > 
> > This patch is currently missing documentation for the
> > .prepare_writeback_job() and .cleanup_writeback_job() operations. I plan
> > to fix this, but first wanted feedback on the direction taken. I'm not
> > entirely happy with the priv pointer in the drm_writeback_job structure,
> > but adding a full state duplicate/destroy machinery for that structure
> > was equally unappealing to me.
> > 
> > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> > index 6fe2303fccd9..70a4886c6e65 100644
> > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > @@ -2245,10 +2245,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
> >  int drm_atomic_helper_prepare_planes(struct drm_device *dev,
> >  				     struct drm_atomic_state *state)
> >  {
> > +	struct drm_connector *connector;
> > +	struct drm_connector_state *new_conn_state;
> >  	struct drm_plane *plane;
> >  	struct drm_plane_state *new_plane_state;
> >  	int ret, i, j;
> >  
> > +	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
> > +		if (!new_conn_state->writeback_job)
> > +			continue;
> > +
> > +		ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> >  	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
> >  		const struct drm_plane_helper_funcs *funcs;
> >  
> > diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
> > index c40889888a16..e802152a01ad 100644
> > --- a/drivers/gpu/drm/drm_atomic_uapi.c
> > +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> > @@ -647,28 +647,15 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> >  	return 0;
> >  }
> >  
> > -static struct drm_writeback_job *
> > -drm_atomic_get_writeback_job(struct drm_connector_state *conn_state)
> > -{
> > -	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> > -
> > -	if (!conn_state->writeback_job)
> > -		conn_state->writeback_job =
> > -			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> > -
> > -	return conn_state->writeback_job;
> > -}
> > -
> >  static int drm_atomic_set_writeback_fb_for_connector(
> >  		struct drm_connector_state *conn_state,
> >  		struct drm_framebuffer *fb)
> >  {
> > -	struct drm_writeback_job *job =
> > -		drm_atomic_get_writeback_job(conn_state);
> > -	if (!job)
> > -		return -ENOMEM;
> > +	int ret;
> >  
> > -	drm_framebuffer_assign(&job->fb, fb);
> > +	ret = drm_writeback_set_fb(conn_state, fb);
> > +	if (ret < 0)
> > +		return ret;
> >  
> >  	if (fb)
> >  		DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n",
> > @@ -1158,19 +1145,17 @@ static int prepare_signaling(struct drm_device *dev,
> >  
> >  	for_each_new_connector_in_state(state, conn, conn_state, i) {
> >  		struct drm_writeback_connector *wb_conn;
> > -		struct drm_writeback_job *job;
> >  		struct drm_out_fence_state *f;
> >  		struct dma_fence *fence;
> >  		s32 __user *fence_ptr;
> >  
> > +		if (!conn_state->writeback_job)
> > +			continue;
> > +
> >  		fence_ptr = get_out_fence_for_connector(state, conn);
> >  		if (!fence_ptr)
> >  			continue;
> >  
> > -		job = drm_atomic_get_writeback_job(conn_state);
> > -		if (!job)
> > -			return -ENOMEM;
> > -
> >  		f = krealloc(*fence_state, sizeof(**fence_state) *
> >  			     (*num_fences + 1), GFP_KERNEL);
> >  		if (!f)
> > @@ -1192,7 +1177,7 @@ static int prepare_signaling(struct drm_device *dev,
> >  			return ret;
> >  		}
> >  
> > -		job->out_fence = fence;
> > +		conn_state->writeback_job->out_fence = fence;
> >  	}
> >  
> >  	/*
> > diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> > index afb1ae6e0ecb..4678d61d634a 100644
> > --- a/drivers/gpu/drm/drm_writeback.c
> > +++ b/drivers/gpu/drm/drm_writeback.c
> > @@ -239,6 +239,42 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >  }
> >  EXPORT_SYMBOL(drm_writeback_connector_init);
> >  
> > +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> > +			 struct drm_framebuffer *fb)
> > +{
> > +	WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
> > +
> > +	if (!conn_state->writeback_job) {
> > +		conn_state->writeback_job =
> > +			kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
> > +		if (!conn_state->writeback_job)
> > +			return -ENOMEM;
> > +
> > +		conn_state->writeback_job->connector =
> > +			drm_connector_to_writeback(conn_state->connector);
> > +	}
> > +
> > +	drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
> > +	return 0;
> > +}
> > +
> > +int drm_writeback_prepare_job(struct drm_writeback_job *job)
> > +{
> > +	struct drm_writeback_connector *connector = job->connector;
> > +	const struct drm_connector_helper_funcs *funcs =
> > +		connector->base.helper_private;
> > +	int ret;
> > +
> > +	if (funcs->cleanup_writeback_job) {
> 
> This feels weird, did you mean to actually check that the funcs->prepare_writeback_job
> hook is implemented?

Good catch. I'll fix this.

> Otherwise, things look good to me!

Thank you.

> > +		ret = funcs->prepare_writeback_job(connector, job);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	job->prepared = true;
> > +	return 0;
> > +}
> > +
> >  /**
> >   * drm_writeback_queue_job - Queue a writeback job for later signalling
> >   * @wb_connector: The writeback connector to queue a job on
> > @@ -275,6 +311,13 @@ EXPORT_SYMBOL(drm_writeback_queue_job);
> >  
> >  void drm_writeback_cleanup_job(struct drm_writeback_job *job)
> >  {
> > +	struct drm_writeback_connector *connector = job->connector;
> > +	const struct drm_connector_helper_funcs *funcs =
> > +		connector->base.helper_private;
> > +
> > +	if (job->prepared && funcs->cleanup_writeback_job)
> > +		funcs->cleanup_writeback_job(connector, job);
> > +
> >  	if (job->fb)
> >  		drm_framebuffer_put(job->fb);
> >  
> > diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> > index 61142aa0ab23..73d03fe66799 100644
> > --- a/include/drm/drm_modeset_helper_vtables.h
> > +++ b/include/drm/drm_modeset_helper_vtables.h
> > @@ -49,6 +49,8 @@
> >   */
> >  
> >  enum mode_set_atomic;
> > +struct drm_writeback_connector;
> > +struct drm_writeback_job;
> >  
> >  /**
> >   * struct drm_crtc_helper_funcs - helper operations for CRTCs
> > @@ -989,6 +991,11 @@ struct drm_connector_helper_funcs {
> >  	 */
> >  	void (*atomic_commit)(struct drm_connector *connector,
> >  			      struct drm_connector_state *state);
> > +
> > +	int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
> > +				     struct drm_writeback_job *job);
> > +	void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
> > +				      struct drm_writeback_job *job);
> >  };
> >  
> >  /**
> > diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> > index 47662c362743..777c14c847f0 100644
> > --- a/include/drm/drm_writeback.h
> > +++ b/include/drm/drm_writeback.h
> > @@ -79,6 +79,20 @@ struct drm_writeback_connector {
> >  };
> >  
> >  struct drm_writeback_job {
> > +	/**
> > +	 * @connector:
> > +	 *
> > +	 * Back-pointer to the writeback connector associated with the job
> > +	 */
> > +	struct drm_writeback_connector *connector;
> > +
> > +	/**
> > +	 * @prepared:
> > +	 *
> > +	 * Set when the job has been prepared with drm_writeback_prepare_job()
> > +	 */
> > +	bool prepared;
> > +
> >  	/**
> >  	 * @cleanup_work:
> >  	 *
> > @@ -98,7 +112,7 @@ struct drm_writeback_job {
> >  	 * @fb:
> >  	 *
> >  	 * Framebuffer to be written to by the writeback connector. Do not set
> > -	 * directly, use drm_atomic_set_writeback_fb_for_connector()
> > +	 * directly, use drm_writeback_set_fb()
> >  	 */
> >  	struct drm_framebuffer *fb;
> >  
> > @@ -108,6 +122,13 @@ struct drm_writeback_job {
> >  	 * Fence which will signal once the writeback has completed
> >  	 */
> >  	struct dma_fence *out_fence;
> > +
> > +	/**
> > +	 * @priv:
> > +	 *
> > +	 * Driver-private data
> > +	 */
> > +	void *priv;
> >  };
> >  
> >  static inline struct drm_writeback_connector *
> > @@ -122,6 +143,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
> >  				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> >  				 const u32 *formats, int n_formats);
> >  
> > +int drm_writeback_set_fb(struct drm_connector_state *conn_state,
> > +			 struct drm_framebuffer *fb);
> > +
> > +int drm_writeback_prepare_job(struct drm_writeback_job *job);
> > +
> >  void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
> >  			     struct drm_connector_state *conn_state);
> >  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-02-22 15:06       ` Brian Starkey
@ 2019-03-05 23:14         ` Laurent Pinchart
  2019-03-06 11:05           ` Brian Starkey
  2019-03-06 14:20           ` Liviu Dudau
  0 siblings, 2 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-05 23:14 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> > On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> >> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> >>> One-shot entries are used as an alternative to committing a complete new
> >>> display list when a couple of registers need to be written for one frame
> >>> and then reset to another value for all subsequent frames. This will be
> >>> used to implement writeback support that will need to enable writeback
> >>> for the duration of a single frame.
> >>> 
> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>> ---
> >>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> >>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> >>>  2 files changed, 81 insertions(+)
> >>> 
> >>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> >>> index 886b3a69d329..7b4d252bfde7 100644
> >>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> >>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> >>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> >>>  
> >>>  	unsigned int num_entries;
> >>>  	unsigned int max_entries;
> >>> +
> >>> +	unsigned int num_patches;
> >>> +	struct {
> >>> +		struct vsp1_dl_entry *entry;
> >>> +		u32 data;
> >>> +	} patches[2];
> >>>  };
> >>>  
> >>>  /**
> >>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> >>>  		return;
> >>>  
> >>>  	dlb->num_entries = 0;
> >>> +	dlb->num_patches = 0;
> >>>  
> >>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> >>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> >>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> >>>  	dlb->num_entries++;
> >>>  }
> >>>  
> >>> +/**
> >>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> >>> + *	single frame
> >>> + * @dlb: The body
> >>> + * @reg: The register address
> >>> + * @value: The register value
> >>> + * @reset_value: The value to reset the register to at the next vblank
> >>> + *
> >>> + * Display lists in continuous mode are re-used by the hardware for successive
> >>> + * frames until a new display list is committed. Changing the VSP configuration
> >>> + * normally requires creating and committing a new display list. This function
> >>> + * offers an alternative race-free way by writing a @value to the @register in
> >>> + * the display list body for a single frame, specifying in @reset_value the
> >>> + * value to reset the register to one vblank after the display list is
> >>> + * committed.
> >>> + *
> >>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> >>> + * and one-shot entries are counted in the total number of entries specified
> >>> + * when the body is allocated by vsp1_dl_body_alloc().
> >>> + */
> >>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>> +				u32 reset_value)
> >>> +{
> >>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> >>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> >>> +		return;
> >>> +
> >>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> >>> +		      "DLB patches size exceeded (max %zu)",
> >>> +		      ARRAY_SIZE(dlb->patches)))
> >>> +		return;
> >>> +
> >>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> >>> +	dlb->patches[dlb->num_patches].data = reset_value;
> >>> +	dlb->num_patches++;
> >>> +
> >>> +	dlb->entries[dlb->num_entries].addr = reg;
> >>> +	dlb->entries[dlb->num_entries].data = value;
> >>> +	dlb->num_entries++;
> >>> +}
> >>> +
> >>>  /* -----------------------------------------------------------------------------
> >>>   * Display List Extended Command Management
> >>>   */
> >>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> >>>  	 * has at least one body, thus we reinitialise the entries list.
> >>>  	 */
> >>>  	dl->body0->num_entries = 0;
> >>> +	dl->body0->num_patches = 0;
> >>>  
> >>>  	list_add_tail(&dl->list, &dl->dlm->free);
> >>>  }
> >>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> >>>   * Display List Manager
> >>>   */
> >>>  
> >>> +/**
> >>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> >>> + *	interrupt
> >>> + * @dlm: the display list manager
> >>> + *
> >>> + * Apply all one-shot patches registered for the active display list.
> >>> + */
> >>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> >>> +{
> >>> +	struct vsp1_dl_body *dlb;
> >>> +	struct vsp1_dl_list *dl;
> >>> +	unsigned int i;
> >>> +
> >>> +	spin_lock(&dlm->lock);
> >>> +
> >>> +	dl = dlm->active;
> >>> +	if (!dl)
> >>> +		goto done;
> >>> +
> >>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> >>> +		for (i = 0; i < dlb->num_patches; ++i)
> >>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> >>> +		dlb->num_patches = 0;
> >>> +	}
> >>> +
> >>> +done:
> >>> +	spin_unlock(&dlm->lock);
> >>> +}
> >>> +
> >> 
> >> We've got some HW which doesn't support one-shot writeback, and use a
> >> similar trick to try and disable writeback immediately after the flip.
> >> 
> >> We ran into issues where the "start" interrupt wouldn't run in time to
> >> make sure the writeback disable was committed before the next frame.
> >> We have to keep track of whether the disable really happened in time,
> >> before we release the output buffer.
> >> 
> >> Might you have a similar problem here?
> > 
> > We may, but there's no provision at the hardware level to check if the
> > configuration updated happened in time. I could add some safety checks
> > but I believe they would be racy in the best case :-(
> 
> We managed to find (what I believe to be...) a non-racy way, but it
> will of course depend a lot on the HW behaviour, so I'll leave it to
> your best judgement.
> 
> We basically have a "configuration committed" interrupt which we can
> use to set a flag indicating writeback was disabled.

The way my hardware is operated is, roughly, as follows:

- The driver prepares a list of values to write to registers and stores
  them in DMA-able memory. This doesn't involve the hardware, and so is
  completely asynchronous to hardware operation.

- The driver then sets a register with the pointer to the registers
  list.

- At the end of the current frame, the hardware reads the memory address
  of the registers list and DMAs values to register. This operation is
  fast and occurs fully during vertical blanking.

- The hardware then waits for the start of the frame, and begins
  processing the framebuffers to send the frame to the display.

- If no new registers list is provided for the next frame, the current
  list is reused.

Two interrupts are provided, one at the start of the frame and one at
the end of the frame. The driver uses the end of frame interrupt to
signal vertical blanking and page flip completion to DRM.

The end of frame interrupt is also used to schedule atomic commits. The
hardware queue depth for atomic commits is just one, so the driver has
to wait until the previous commit has been processed before writing the
new registers list address to the hardware. To solve the race between
the end of frame interrupt and the address write, the hardware provides
a status bit that is set to 1 when the address is written, and reset to
0 when the hardware starts processing the registers list.

We thus have three states for an atomic commit:

- active, where the corresponding registers list address has been
  written to the hardware, and processed

- queued, where the corresponding registers list address has been
  written to the hardware but not processed yet

- pending, where the corresponding registers list address hasn't been
  written to the hardware yet

The status bit mentioned above allows us to tell if a list exists in the
queued state.

At frame end time, if the status bit is set, we have potentially lost
the race between writing the new registers list and the frame end
interrupt, so we wait for one more vblank. Otherwise, if a list was
queued, we move it to the active state, and retire the active list. If a
list was pending, we write its address to the hardware, and move it to
the queued state.

To schedule writeback I ended up using the frame start interrupt.
Writeback has a specific need in that it requires enabling the memory
write interface for a single frame, and that is not something the
hardware supports directly. I can't either queue a new registers list
with the write interface disabled right after the list with the
writeback request became active, as any atomic commit would then be
delayed by an extra frame. I thus ended up modifying the registers list
in place at frame start time, which is right after the active register
list has been DMA'ed to hardware registers. We have the duration of a
whole frame to do so, before the hardware transfers the same list again
at the beginning of the next frame.

There is a race condition there, as the in-place modification of the
active list could be done after the end of the frame if the interrupt
latency gets too large. I don't see how I could solve that, as I could
write the value after the hardware starts processing the active list at
frame end time, but before the interrupt is notified.

> > Note that we have the duration of a complete frame to disable writeback,
> > as we receive an interrupt when the frame starts, and have until vblank
> > to update the configuration. It's thus slightly better than having to
> > disable writeback between vblank and the start of the next frame.
> 
> Yeah... we have a whole frame too. I'm struggling to find our wiki
> page with the data, but anecdotally there's some (out-of-tree) drivers
> which keep interrupts masked for a _really long_ time. It's nice if
> you don't have to care about those :-)

I only provide two options: not my problem, or give me the source code
and documentation and let me upstream a clean version of the out-of-tree
drivers :-)

> >>>  /**
> >>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> >>>   * @dlm: the display list manager
> >>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> >>> index e0fdb145e6ed..f845607abc4c 100644
> >>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> >>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> >>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> >>>  					unsigned int prealloc);
> >>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> >>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> >>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> >>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> >>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> >>>  
> >>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> >>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> >>>  
> >>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> >>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>> +				u32 reset_value);
> >>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> >>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> >>>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-05 23:14         ` Laurent Pinchart
@ 2019-03-06 11:05           ` Brian Starkey
  2019-03-06 18:22             ` Laurent Pinchart
  2019-03-06 14:20           ` Liviu Dudau
  1 sibling, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-03-06 11:05 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi,

On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> Hi Brian,
> 
> On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> > On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> > > On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> > >> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > >>> One-shot entries are used as an alternative to committing a complete new
> > >>> display list when a couple of registers need to be written for one frame
> > >>> and then reset to another value for all subsequent frames. This will be
> > >>> used to implement writeback support that will need to enable writeback
> > >>> for the duration of a single frame.
> > >>> 
> > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > >>> ---
> > >>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> > >>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> > >>>  2 files changed, 81 insertions(+)
> > >>> 
> > >>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>> index 886b3a69d329..7b4d252bfde7 100644
> > >>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > >>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> > >>>  
> > >>>  	unsigned int num_entries;
> > >>>  	unsigned int max_entries;
> > >>> +
> > >>> +	unsigned int num_patches;
> > >>> +	struct {
> > >>> +		struct vsp1_dl_entry *entry;
> > >>> +		u32 data;
> > >>> +	} patches[2];
> > >>>  };
> > >>>  
> > >>>  /**
> > >>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> > >>>  		return;
> > >>>  
> > >>>  	dlb->num_entries = 0;
> > >>> +	dlb->num_patches = 0;
> > >>>  
> > >>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> > >>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> > >>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> > >>>  	dlb->num_entries++;
> > >>>  }
> > >>>  
> > >>> +/**
> > >>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > >>> + *	single frame
> > >>> + * @dlb: The body
> > >>> + * @reg: The register address
> > >>> + * @value: The register value
> > >>> + * @reset_value: The value to reset the register to at the next vblank
> > >>> + *
> > >>> + * Display lists in continuous mode are re-used by the hardware for successive
> > >>> + * frames until a new display list is committed. Changing the VSP configuration
> > >>> + * normally requires creating and committing a new display list. This function
> > >>> + * offers an alternative race-free way by writing a @value to the @register in
> > >>> + * the display list body for a single frame, specifying in @reset_value the
> > >>> + * value to reset the register to one vblank after the display list is
> > >>> + * committed.
> > >>> + *
> > >>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> > >>> + * and one-shot entries are counted in the total number of entries specified
> > >>> + * when the body is allocated by vsp1_dl_body_alloc().
> > >>> + */
> > >>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>> +				u32 reset_value)
> > >>> +{
> > >>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > >>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> > >>> +		return;
> > >>> +
> > >>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > >>> +		      "DLB patches size exceeded (max %zu)",
> > >>> +		      ARRAY_SIZE(dlb->patches)))
> > >>> +		return;
> > >>> +
> > >>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > >>> +	dlb->patches[dlb->num_patches].data = reset_value;
> > >>> +	dlb->num_patches++;
> > >>> +
> > >>> +	dlb->entries[dlb->num_entries].addr = reg;
> > >>> +	dlb->entries[dlb->num_entries].data = value;
> > >>> +	dlb->num_entries++;
> > >>> +}
> > >>> +
> > >>>  /* -----------------------------------------------------------------------------
> > >>>   * Display List Extended Command Management
> > >>>   */
> > >>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> > >>>  	 * has at least one body, thus we reinitialise the entries list.
> > >>>  	 */
> > >>>  	dl->body0->num_entries = 0;
> > >>> +	dl->body0->num_patches = 0;
> > >>>  
> > >>>  	list_add_tail(&dl->list, &dl->dlm->free);
> > >>>  }
> > >>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> > >>>   * Display List Manager
> > >>>   */
> > >>>  
> > >>> +/**
> > >>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > >>> + *	interrupt
> > >>> + * @dlm: the display list manager
> > >>> + *
> > >>> + * Apply all one-shot patches registered for the active display list.
> > >>> + */
> > >>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > >>> +{
> > >>> +	struct vsp1_dl_body *dlb;
> > >>> +	struct vsp1_dl_list *dl;
> > >>> +	unsigned int i;
> > >>> +
> > >>> +	spin_lock(&dlm->lock);
> > >>> +
> > >>> +	dl = dlm->active;
> > >>> +	if (!dl)
> > >>> +		goto done;
> > >>> +
> > >>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> > >>> +		for (i = 0; i < dlb->num_patches; ++i)
> > >>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> > >>> +		dlb->num_patches = 0;
> > >>> +	}
> > >>> +
> > >>> +done:
> > >>> +	spin_unlock(&dlm->lock);
> > >>> +}
> > >>> +
> > >> 
> > >> We've got some HW which doesn't support one-shot writeback, and use a
> > >> similar trick to try and disable writeback immediately after the flip.
> > >> 
> > >> We ran into issues where the "start" interrupt wouldn't run in time to
> > >> make sure the writeback disable was committed before the next frame.
> > >> We have to keep track of whether the disable really happened in time,
> > >> before we release the output buffer.
> > >> 
> > >> Might you have a similar problem here?
> > > 
> > > We may, but there's no provision at the hardware level to check if the
> > > configuration updated happened in time. I could add some safety checks
> > > but I believe they would be racy in the best case :-(
> > 
> > We managed to find (what I believe to be...) a non-racy way, but it
> > will of course depend a lot on the HW behaviour, so I'll leave it to
> > your best judgement.
> > 
> > We basically have a "configuration committed" interrupt which we can
> > use to set a flag indicating writeback was disabled.
> 
> The way my hardware is operated is, roughly, as follows:

Thanks for the detailed explanation.

> 
> - The driver prepares a list of values to write to registers and stores
>   them in DMA-able memory. This doesn't involve the hardware, and so is
>   completely asynchronous to hardware operation.
> 
> - The driver then sets a register with the pointer to the registers
>   list.
> 
> - At the end of the current frame, the hardware reads the memory address
>   of the registers list and DMAs values to register. This operation is
>   fast and occurs fully during vertical blanking.
> 
> - The hardware then waits for the start of the frame, and begins
>   processing the framebuffers to send the frame to the display.
> 
> - If no new registers list is provided for the next frame, the current
>   list is reused.

Does it have to be fetched again, or the HW can use the copy that's in
registers already? Does it have to fetch a register list every frame
when the display is active, or you can stop it fetching somehow?

> 
> Two interrupts are provided, one at the start of the frame and one at
> the end of the frame. The driver uses the end of frame interrupt to
> signal vertical blanking and page flip completion to DRM.

Just for my understanding - you signal the page flip at the point
where the HW fetches the register list containing the scene being
flipped to? Or you signal it after that scene has been on-screen for
one frame?

> 
> The end of frame interrupt is also used to schedule atomic commits. The
> hardware queue depth for atomic commits is just one, so the driver has
> to wait until the previous commit has been processed before writing the
> new registers list address to the hardware. To solve the race between
> the end of frame interrupt and the address write, the hardware provides
> a status bit that is set to 1 when the address is written, and reset to
> 0 when the hardware starts processing the registers list.
> 
> We thus have three states for an atomic commit:
> 
> - active, where the corresponding registers list address has been
>   written to the hardware, and processed
> 
> - queued, where the corresponding registers list address has been
>   written to the hardware but not processed yet
> 
> - pending, where the corresponding registers list address hasn't been
>   written to the hardware yet
> 
> The status bit mentioned above allows us to tell if a list exists in the
> queued state.
> 
> At frame end time, if the status bit is set, we have potentially lost
> the race between writing the new registers list and the frame end
> interrupt, so we wait for one more vblank. Otherwise, if a list was
> queued, we move it to the active state, and retire the active list. If a
> list was pending, we write its address to the hardware, and move it to
> the queued state.
> 
> To schedule writeback I ended up using the frame start interrupt.
> Writeback has a specific need in that it requires enabling the memory
> write interface for a single frame, and that is not something the
> hardware supports directly. I can't either queue a new registers list
> with the write interface disabled right after the list with the
> writeback request became active, as any atomic commit would then be
> delayed by an extra frame. I thus ended up modifying the registers list
> in place at frame start time, which is right after the active register
> list has been DMA'ed to hardware registers. We have the duration of a
> whole frame to do so, before the hardware transfers the same list again
> at the beginning of the next frame.
> 
> There is a race condition there, as the in-place modification of the
> active list could be done after the end of the frame if the interrupt
> latency gets too large. I don't see how I could solve that, as I could
> write the value after the hardware starts processing the active list at
> frame end time, but before the interrupt is notified.

So you can be totally safe by making a copy of the register list,
disabling writeback there, and signalling writeback completion when
the HW status bit tells you that new register list has become active.
The issue is that it effectively halves the maximum update rate when
writeback is enabled?

It sounds like the HW doesn't really give you a way to "cancel" a
queued register list, which is a bit unfortunate. You don't happen to
have a DMA engine trigger or something you could use to do the
register list modification at a guaranteed time do you?

Are you always going to be protected by an IOMMU, preventing the
writeback from trashing physical memory? If that's not the case, then
the race can have pretty dire consequences.

> 
> > > Note that we have the duration of a complete frame to disable writeback,
> > > as we receive an interrupt when the frame starts, and have until vblank
> > > to update the configuration. It's thus slightly better than having to
> > > disable writeback between vblank and the start of the next frame.
> > 
> > Yeah... we have a whole frame too. I'm struggling to find our wiki
> > page with the data, but anecdotally there's some (out-of-tree) drivers
> > which keep interrupts masked for a _really long_ time. It's nice if
> > you don't have to care about those :-)
> 
> I only provide two options: not my problem, or give me the source code
> and documentation and let me upstream a clean version of the out-of-tree
> drivers :-)

This was a case of drivers "in-the-wild" - I don't know the specifics,
but they weren't Arm drivers. I was just sharing as a data point.

Thanks,
-Brian

> 
> > >>>  /**
> > >>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> > >>>   * @dlm: the display list manager
> > >>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>> index e0fdb145e6ed..f845607abc4c 100644
> > >>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > >>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> > >>>  					unsigned int prealloc);
> > >>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> > >>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > >>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> > >>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> > >>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> > >>>  
> > >>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> > >>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> > >>>  
> > >>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > >>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>> +				u32 reset_value);
> > >>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> > >>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> > >>>  
> 
> -- 
> Regards,
> 
> Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-05 23:14         ` Laurent Pinchart
  2019-03-06 11:05           ` Brian Starkey
@ 2019-03-06 14:20           ` Liviu Dudau
  2019-03-06 18:01             ` Laurent Pinchart
  1 sibling, 1 reply; 65+ messages in thread
From: Liviu Dudau @ 2019-03-06 14:20 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> Hi Brian,
> 
> On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> > On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> > > On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> > >> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > >>> One-shot entries are used as an alternative to committing a complete new
> > >>> display list when a couple of registers need to be written for one frame
> > >>> and then reset to another value for all subsequent frames. This will be
> > >>> used to implement writeback support that will need to enable writeback
> > >>> for the duration of a single frame.
> > >>> 
> > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > >>> ---
> > >>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> > >>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> > >>>  2 files changed, 81 insertions(+)
> > >>> 
> > >>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>> index 886b3a69d329..7b4d252bfde7 100644
> > >>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > >>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> > >>>  
> > >>>  	unsigned int num_entries;
> > >>>  	unsigned int max_entries;
> > >>> +
> > >>> +	unsigned int num_patches;
> > >>> +	struct {
> > >>> +		struct vsp1_dl_entry *entry;
> > >>> +		u32 data;
> > >>> +	} patches[2];
> > >>>  };
> > >>>  
> > >>>  /**
> > >>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> > >>>  		return;
> > >>>  
> > >>>  	dlb->num_entries = 0;
> > >>> +	dlb->num_patches = 0;
> > >>>  
> > >>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> > >>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> > >>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> > >>>  	dlb->num_entries++;
> > >>>  }
> > >>>  
> > >>> +/**
> > >>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > >>> + *	single frame
> > >>> + * @dlb: The body
> > >>> + * @reg: The register address
> > >>> + * @value: The register value
> > >>> + * @reset_value: The value to reset the register to at the next vblank
> > >>> + *
> > >>> + * Display lists in continuous mode are re-used by the hardware for successive
> > >>> + * frames until a new display list is committed. Changing the VSP configuration
> > >>> + * normally requires creating and committing a new display list. This function
> > >>> + * offers an alternative race-free way by writing a @value to the @register in
> > >>> + * the display list body for a single frame, specifying in @reset_value the
> > >>> + * value to reset the register to one vblank after the display list is
> > >>> + * committed.
> > >>> + *
> > >>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> > >>> + * and one-shot entries are counted in the total number of entries specified
> > >>> + * when the body is allocated by vsp1_dl_body_alloc().
> > >>> + */
> > >>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>> +				u32 reset_value)
> > >>> +{
> > >>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > >>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> > >>> +		return;
> > >>> +
> > >>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > >>> +		      "DLB patches size exceeded (max %zu)",
> > >>> +		      ARRAY_SIZE(dlb->patches)))
> > >>> +		return;
> > >>> +
> > >>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > >>> +	dlb->patches[dlb->num_patches].data = reset_value;
> > >>> +	dlb->num_patches++;
> > >>> +
> > >>> +	dlb->entries[dlb->num_entries].addr = reg;
> > >>> +	dlb->entries[dlb->num_entries].data = value;
> > >>> +	dlb->num_entries++;
> > >>> +}
> > >>> +
> > >>>  /* -----------------------------------------------------------------------------
> > >>>   * Display List Extended Command Management
> > >>>   */
> > >>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> > >>>  	 * has at least one body, thus we reinitialise the entries list.
> > >>>  	 */
> > >>>  	dl->body0->num_entries = 0;
> > >>> +	dl->body0->num_patches = 0;
> > >>>  
> > >>>  	list_add_tail(&dl->list, &dl->dlm->free);
> > >>>  }
> > >>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> > >>>   * Display List Manager
> > >>>   */
> > >>>  
> > >>> +/**
> > >>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > >>> + *	interrupt
> > >>> + * @dlm: the display list manager
> > >>> + *
> > >>> + * Apply all one-shot patches registered for the active display list.
> > >>> + */
> > >>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > >>> +{
> > >>> +	struct vsp1_dl_body *dlb;
> > >>> +	struct vsp1_dl_list *dl;
> > >>> +	unsigned int i;
> > >>> +
> > >>> +	spin_lock(&dlm->lock);
> > >>> +
> > >>> +	dl = dlm->active;
> > >>> +	if (!dl)
> > >>> +		goto done;
> > >>> +
> > >>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> > >>> +		for (i = 0; i < dlb->num_patches; ++i)
> > >>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> > >>> +		dlb->num_patches = 0;
> > >>> +	}
> > >>> +
> > >>> +done:
> > >>> +	spin_unlock(&dlm->lock);
> > >>> +}
> > >>> +
> > >> 
> > >> We've got some HW which doesn't support one-shot writeback, and use a
> > >> similar trick to try and disable writeback immediately after the flip.
> > >> 
> > >> We ran into issues where the "start" interrupt wouldn't run in time to
> > >> make sure the writeback disable was committed before the next frame.
> > >> We have to keep track of whether the disable really happened in time,
> > >> before we release the output buffer.
> > >> 
> > >> Might you have a similar problem here?
> > > 
> > > We may, but there's no provision at the hardware level to check if the
> > > configuration updated happened in time. I could add some safety checks
> > > but I believe they would be racy in the best case :-(
> > 
> > We managed to find (what I believe to be...) a non-racy way, but it
> > will of course depend a lot on the HW behaviour, so I'll leave it to
> > your best judgement.
> > 
> > We basically have a "configuration committed" interrupt which we can
> > use to set a flag indicating writeback was disabled.
> 
> The way my hardware is operated is, roughly, as follows:
> 
> - The driver prepares a list of values to write to registers and stores
>   them in DMA-able memory. This doesn't involve the hardware, and so is
>   completely asynchronous to hardware operation.
> 
> - The driver then sets a register with the pointer to the registers
>   list.
> 
> - At the end of the current frame, the hardware reads the memory address
>   of the registers list and DMAs values to register. This operation is
>   fast and occurs fully during vertical blanking.
> 
> - The hardware then waits for the start of the frame, and begins
>   processing the framebuffers to send the frame to the display.
> 
> - If no new registers list is provided for the next frame, the current
>   list is reused.

Does this mean the hardware goes back to step 3 and re-reads the memory
using DMA?

> 
> Two interrupts are provided, one at the start of the frame and one at
> the end of the frame. The driver uses the end of frame interrupt to
> signal vertical blanking and page flip completion to DRM.
> 
> The end of frame interrupt is also used to schedule atomic commits. The
> hardware queue depth for atomic commits is just one, so the driver has
> to wait until the previous commit has been processed before writing the
> new registers list address to the hardware. To solve the race between
> the end of frame interrupt and the address write, the hardware provides
> a status bit that is set to 1 when the address is written, and reset to
> 0 when the hardware starts processing the registers list.

For my understanding, the following simplication is then true: status = 0
means driver is allowed to update the pointer register, status = 1 means
currently programmed pointer is used to fetch the register values, right?
What happens if you update the pointer while status = 1? DMA transfer gets
interrupted and a new one is scheduled? Does the DMA transfer get cancelled?

> 
> We thus have three states for an atomic commit:
> 
> - active, where the corresponding registers list address has been
>   written to the hardware, and processed
> 
> - queued, where the corresponding registers list address has been
>   written to the hardware but not processed yet
> 
> - pending, where the corresponding registers list address hasn't been
>   written to the hardware yet
> 
> The status bit mentioned above allows us to tell if a list exists in the
> queued state.
> 
> At frame end time, if the status bit is set, we have potentially lost
> the race between writing the new registers list and the frame end
> interrupt, so we wait for one more vblank. Otherwise, if a list was
> queued, we move it to the active state, and retire the active list. If a
> list was pending, we write its address to the hardware, and move it to
> the queued state.
> 
> To schedule writeback I ended up using the frame start interrupt.
> Writeback has a specific need in that it requires enabling the memory
> write interface for a single frame, and that is not something the
> hardware supports directly. I can't either queue a new registers list
> with the write interface disabled right after the list with the
> writeback request became active, as any atomic commit would then be
> delayed by an extra frame. I thus ended up modifying the registers list
> in place at frame start time, which is right after the active register
> list has been DMA'ed to hardware registers. We have the duration of a
> whole frame to do so, before the hardware transfers the same list again
> at the beginning of the next frame.

Yes, that is like to what we do for DP500 in mali-dp, where we have a
similar situation. HW will continue to stream to the given buffer until
stopped, but the writeback API is one-shot mode, so on vblank (when we
know the writeback will start at the beginning of the new frame) we
program the memwrite to stop, but that only takes effect when we also
program the GO bit (CONFIG_VALID in our case).

One problem we had to take care for DP500 was when the next commit (the
queued one in your case) also contains a writeback buffer. In that case,
we don't stop the writeback but "re-start" it.

> 
> There is a race condition there, as the in-place modification of the
> active list could be done after the end of the frame if the interrupt
> latency gets too large. I don't see how I could solve that, as I could
> write the value after the hardware starts processing the active list at
> frame end time, but before the interrupt is notified.

I think that would be a better idea if the HW finishes to transfer
before the end of frame interrupt is signalled. Otherwise if your
interrupt handler is scheduled too fast, you might overwrite the active
frame values?

Best regards,
Liviu

> 
> > > Note that we have the duration of a complete frame to disable writeback,
> > > as we receive an interrupt when the frame starts, and have until vblank
> > > to update the configuration. It's thus slightly better than having to
> > > disable writeback between vblank and the start of the next frame.
> > 
> > Yeah... we have a whole frame too. I'm struggling to find our wiki
> > page with the data, but anecdotally there's some (out-of-tree) drivers
> > which keep interrupts masked for a _really long_ time. It's nice if
> > you don't have to care about those :-)
> 
> I only provide two options: not my problem, or give me the source code
> and documentation and let me upstream a clean version of the out-of-tree
> drivers :-)
> 
> > >>>  /**
> > >>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> > >>>   * @dlm: the display list manager
> > >>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>> index e0fdb145e6ed..f845607abc4c 100644
> > >>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > >>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> > >>>  					unsigned int prealloc);
> > >>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> > >>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > >>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> > >>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> > >>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> > >>>  
> > >>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> > >>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> > >>>  
> > >>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > >>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>> +				u32 reset_value);
> > >>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> > >>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> > >>>  
> 
> -- 
> Regards,
> 
> Laurent Pinchart

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-06 14:20           ` Liviu Dudau
@ 2019-03-06 18:01             ` Laurent Pinchart
  2019-03-07 11:52               ` Liviu Dudau
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-06 18:01 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Liviu,

On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> > On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> >> On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> >>> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> >>>> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> >>>>> One-shot entries are used as an alternative to committing a complete new
> >>>>> display list when a couple of registers need to be written for one frame
> >>>>> and then reset to another value for all subsequent frames. This will be
> >>>>> used to implement writeback support that will need to enable writeback
> >>>>> for the duration of a single frame.
> >>>>> 
> >>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>>>> ---
> >>>>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> >>>>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> >>>>>  2 files changed, 81 insertions(+)
> >>>>> 
> >>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>> index 886b3a69d329..7b4d252bfde7 100644
> >>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> >>>>>  
> >>>>>  	unsigned int num_entries;
> >>>>>  	unsigned int max_entries;
> >>>>> +
> >>>>> +	unsigned int num_patches;
> >>>>> +	struct {
> >>>>> +		struct vsp1_dl_entry *entry;
> >>>>> +		u32 data;
> >>>>> +	} patches[2];
> >>>>>  };
> >>>>>  
> >>>>>  /**
> >>>>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> >>>>>  		return;
> >>>>>  
> >>>>>  	dlb->num_entries = 0;
> >>>>> +	dlb->num_patches = 0;
> >>>>>  
> >>>>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> >>>>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> >>>>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> >>>>>  	dlb->num_entries++;
> >>>>>  }
> >>>>>  
> >>>>> +/**
> >>>>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> >>>>> + *	single frame
> >>>>> + * @dlb: The body
> >>>>> + * @reg: The register address
> >>>>> + * @value: The register value
> >>>>> + * @reset_value: The value to reset the register to at the next vblank
> >>>>> + *
> >>>>> + * Display lists in continuous mode are re-used by the hardware for successive
> >>>>> + * frames until a new display list is committed. Changing the VSP configuration
> >>>>> + * normally requires creating and committing a new display list. This function
> >>>>> + * offers an alternative race-free way by writing a @value to the @register in
> >>>>> + * the display list body for a single frame, specifying in @reset_value the
> >>>>> + * value to reset the register to one vblank after the display list is
> >>>>> + * committed.
> >>>>> + *
> >>>>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> >>>>> + * and one-shot entries are counted in the total number of entries specified
> >>>>> + * when the body is allocated by vsp1_dl_body_alloc().
> >>>>> + */
> >>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>>>> +				u32 reset_value)
> >>>>> +{
> >>>>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> >>>>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> >>>>> +		return;
> >>>>> +
> >>>>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> >>>>> +		      "DLB patches size exceeded (max %zu)",
> >>>>> +		      ARRAY_SIZE(dlb->patches)))
> >>>>> +		return;
> >>>>> +
> >>>>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> >>>>> +	dlb->patches[dlb->num_patches].data = reset_value;
> >>>>> +	dlb->num_patches++;
> >>>>> +
> >>>>> +	dlb->entries[dlb->num_entries].addr = reg;
> >>>>> +	dlb->entries[dlb->num_entries].data = value;
> >>>>> +	dlb->num_entries++;
> >>>>> +}
> >>>>> +
> >>>>>  /* -----------------------------------------------------------------------------
> >>>>>   * Display List Extended Command Management
> >>>>>   */
> >>>>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> >>>>>  	 * has at least one body, thus we reinitialise the entries list.
> >>>>>  	 */
> >>>>>  	dl->body0->num_entries = 0;
> >>>>> +	dl->body0->num_patches = 0;
> >>>>>  
> >>>>>  	list_add_tail(&dl->list, &dl->dlm->free);
> >>>>>  }
> >>>>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> >>>>>   * Display List Manager
> >>>>>   */
> >>>>>  
> >>>>> +/**
> >>>>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> >>>>> + *	interrupt
> >>>>> + * @dlm: the display list manager
> >>>>> + *
> >>>>> + * Apply all one-shot patches registered for the active display list.
> >>>>> + */
> >>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> >>>>> +{
> >>>>> +	struct vsp1_dl_body *dlb;
> >>>>> +	struct vsp1_dl_list *dl;
> >>>>> +	unsigned int i;
> >>>>> +
> >>>>> +	spin_lock(&dlm->lock);
> >>>>> +
> >>>>> +	dl = dlm->active;
> >>>>> +	if (!dl)
> >>>>> +		goto done;
> >>>>> +
> >>>>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> >>>>> +		for (i = 0; i < dlb->num_patches; ++i)
> >>>>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> >>>>> +		dlb->num_patches = 0;
> >>>>> +	}
> >>>>> +
> >>>>> +done:
> >>>>> +	spin_unlock(&dlm->lock);
> >>>>> +}
> >>>>> +
> >>>> 
> >>>> We've got some HW which doesn't support one-shot writeback, and use a
> >>>> similar trick to try and disable writeback immediately after the flip.
> >>>> 
> >>>> We ran into issues where the "start" interrupt wouldn't run in time to
> >>>> make sure the writeback disable was committed before the next frame.
> >>>> We have to keep track of whether the disable really happened in time,
> >>>> before we release the output buffer.
> >>>> 
> >>>> Might you have a similar problem here?
> >>> 
> >>> We may, but there's no provision at the hardware level to check if the
> >>> configuration updated happened in time. I could add some safety checks
> >>> but I believe they would be racy in the best case :-(
> >> 
> >> We managed to find (what I believe to be...) a non-racy way, but it
> >> will of course depend a lot on the HW behaviour, so I'll leave it to
> >> your best judgement.
> >> 
> >> We basically have a "configuration committed" interrupt which we can
> >> use to set a flag indicating writeback was disabled.
> > 
> > The way my hardware is operated is, roughly, as follows:
> > 
> > - The driver prepares a list of values to write to registers and stores
> >   them in DMA-able memory. This doesn't involve the hardware, and so is
> >   completely asynchronous to hardware operation.
> > 
> > - The driver then sets a register with the pointer to the registers
> >   list.
> > 
> > - At the end of the current frame, the hardware reads the memory address
> >   of the registers list and DMAs values to register. This operation is
> >   fast and occurs fully during vertical blanking.
> > 
> > - The hardware then waits for the start of the frame, and begins
> >   processing the framebuffers to send the frame to the display.
> > 
> > - If no new registers list is provided for the next frame, the current
> >   list is reused.
> 
> Does this mean the hardware goes back to step 3 and re-reads the memory
> using DMA?

That's correct. At frame end the hardware will always read and apply a
registers list, either a new one or the old one if no new list is
programmed.

> > Two interrupts are provided, one at the start of the frame and one at
> > the end of the frame. The driver uses the end of frame interrupt to
> > signal vertical blanking and page flip completion to DRM.
> > 
> > The end of frame interrupt is also used to schedule atomic commits. The
> > hardware queue depth for atomic commits is just one, so the driver has
> > to wait until the previous commit has been processed before writing the
> > new registers list address to the hardware. To solve the race between
> > the end of frame interrupt and the address write, the hardware provides
> > a status bit that is set to 1 when the address is written, and reset to
> > 0 when the hardware starts processing the registers list.
> 
> For my understanding, the following simplication is then true: status = 0
> means driver is allowed to update the pointer register, status = 1 means
> currently programmed pointer is used to fetch the register values, right?

Almost. status = 1 means that the pointer has been programmed by the
CPU, but not taken into account by the hardware yet. status = 0 means
that the pointer hasn't been written to by the CPU since the last time
the hardware read it.

> What happens if you update the pointer while status = 1? DMA transfer gets
> interrupted and a new one is scheduled? Does the DMA transfer get cancelled?

You should not do that, as it's racy. The hardware will use either the
old value of the pointer or new new one, based on the timings of the
reprogrammation, and you can't tell which one is used. Nothing can
cancel the DMA, when it's time to start the DMA the hardware will
transfer the value of the pointer to an internal register and use that
for DMA. Reprogramming the pointer during the DMA will not affect the
DMA, the new value will be taken into account for the next DMA.

> > We thus have three states for an atomic commit:
> > 
> > - active, where the corresponding registers list address has been
> >   written to the hardware, and processed
> > 
> > - queued, where the corresponding registers list address has been
> >   written to the hardware but not processed yet
> > 
> > - pending, where the corresponding registers list address hasn't been
> >   written to the hardware yet
> > 
> > The status bit mentioned above allows us to tell if a list exists in the
> > queued state.
> > 
> > At frame end time, if the status bit is set, we have potentially lost
> > the race between writing the new registers list and the frame end
> > interrupt, so we wait for one more vblank. Otherwise, if a list was
> > queued, we move it to the active state, and retire the active list. If a
> > list was pending, we write its address to the hardware, and move it to
> > the queued state.
> > 
> > To schedule writeback I ended up using the frame start interrupt.
> > Writeback has a specific need in that it requires enabling the memory
> > write interface for a single frame, and that is not something the
> > hardware supports directly. I can't either queue a new registers list
> > with the write interface disabled right after the list with the
> > writeback request became active, as any atomic commit would then be
> > delayed by an extra frame. I thus ended up modifying the registers list
> > in place at frame start time, which is right after the active register
> > list has been DMA'ed to hardware registers. We have the duration of a
> > whole frame to do so, before the hardware transfers the same list again
> > at the beginning of the next frame.
> 
> Yes, that is like to what we do for DP500 in mali-dp, where we have a
> similar situation. HW will continue to stream to the given buffer until
> stopped, but the writeback API is one-shot mode, so on vblank (when we
> know the writeback will start at the beginning of the new frame) we
> program the memwrite to stop, but that only takes effect when we also
> program the GO bit (CONFIG_VALID in our case).
> 
> One problem we had to take care for DP500 was when the next commit (the
> queued one in your case) also contains a writeback buffer. In that case,
> we don't stop the writeback but "re-start" it.
> 
> > There is a race condition there, as the in-place modification of the
> > active list could be done after the end of the frame if the interrupt
> > latency gets too large. I don't see how I could solve that, as I could
> > write the value after the hardware starts processing the active list at
> > frame end time, but before the interrupt is notified.
> 
> I think that would be a better idea if the HW finishes to transfer
> before the end of frame interrupt is signalled. Otherwise if your
> interrupt handler is scheduled too fast, you might overwrite the active
> frame values?

That can't happen, as I patch the active registers list in-place in the
frame start interrupt handler, and frame start occurs after the hardware
finishes the DMA of the registers list.

> >>> Note that we have the duration of a complete frame to disable writeback,
> >>> as we receive an interrupt when the frame starts, and have until vblank
> >>> to update the configuration. It's thus slightly better than having to
> >>> disable writeback between vblank and the start of the next frame.
> >> 
> >> Yeah... we have a whole frame too. I'm struggling to find our wiki
> >> page with the data, but anecdotally there's some (out-of-tree) drivers
> >> which keep interrupts masked for a _really long_ time. It's nice if
> >> you don't have to care about those :-)
> > 
> > I only provide two options: not my problem, or give me the source code
> > and documentation and let me upstream a clean version of the out-of-tree
> > drivers :-)
> > 
> >>>>>  /**
> >>>>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> >>>>>   * @dlm: the display list manager
> >>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>> index e0fdb145e6ed..f845607abc4c 100644
> >>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> >>>>>  					unsigned int prealloc);
> >>>>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> >>>>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> >>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> >>>>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> >>>>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> >>>>>  
> >>>>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> >>>>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> >>>>>  
> >>>>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> >>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>>>> +				u32 reset_value);
> >>>>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> >>>>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> >>>>>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-06 11:05           ` Brian Starkey
@ 2019-03-06 18:22             ` Laurent Pinchart
  2019-03-07 12:28               ` Brian Starkey
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-06 18:22 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Wed, Mar 06, 2019 at 11:05:05AM +0000, Brian Starkey wrote:
> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> > On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> >> On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> >>> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> >>>> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> >>>>> One-shot entries are used as an alternative to committing a complete new
> >>>>> display list when a couple of registers need to be written for one frame
> >>>>> and then reset to another value for all subsequent frames. This will be
> >>>>> used to implement writeback support that will need to enable writeback
> >>>>> for the duration of a single frame.
> >>>>> 
> >>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>>>> ---
> >>>>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> >>>>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> >>>>>  2 files changed, 81 insertions(+)
> >>>>> 
> >>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>> index 886b3a69d329..7b4d252bfde7 100644
> >>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> >>>>>  
> >>>>>  	unsigned int num_entries;
> >>>>>  	unsigned int max_entries;
> >>>>> +
> >>>>> +	unsigned int num_patches;
> >>>>> +	struct {
> >>>>> +		struct vsp1_dl_entry *entry;
> >>>>> +		u32 data;
> >>>>> +	} patches[2];
> >>>>>  };
> >>>>>  
> >>>>>  /**
> >>>>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> >>>>>  		return;
> >>>>>  
> >>>>>  	dlb->num_entries = 0;
> >>>>> +	dlb->num_patches = 0;
> >>>>>  
> >>>>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> >>>>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> >>>>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> >>>>>  	dlb->num_entries++;
> >>>>>  }
> >>>>>  
> >>>>> +/**
> >>>>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> >>>>> + *	single frame
> >>>>> + * @dlb: The body
> >>>>> + * @reg: The register address
> >>>>> + * @value: The register value
> >>>>> + * @reset_value: The value to reset the register to at the next vblank
> >>>>> + *
> >>>>> + * Display lists in continuous mode are re-used by the hardware for successive
> >>>>> + * frames until a new display list is committed. Changing the VSP configuration
> >>>>> + * normally requires creating and committing a new display list. This function
> >>>>> + * offers an alternative race-free way by writing a @value to the @register in
> >>>>> + * the display list body for a single frame, specifying in @reset_value the
> >>>>> + * value to reset the register to one vblank after the display list is
> >>>>> + * committed.
> >>>>> + *
> >>>>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> >>>>> + * and one-shot entries are counted in the total number of entries specified
> >>>>> + * when the body is allocated by vsp1_dl_body_alloc().
> >>>>> + */
> >>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>>>> +				u32 reset_value)
> >>>>> +{
> >>>>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> >>>>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> >>>>> +		return;
> >>>>> +
> >>>>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> >>>>> +		      "DLB patches size exceeded (max %zu)",
> >>>>> +		      ARRAY_SIZE(dlb->patches)))
> >>>>> +		return;
> >>>>> +
> >>>>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> >>>>> +	dlb->patches[dlb->num_patches].data = reset_value;
> >>>>> +	dlb->num_patches++;
> >>>>> +
> >>>>> +	dlb->entries[dlb->num_entries].addr = reg;
> >>>>> +	dlb->entries[dlb->num_entries].data = value;
> >>>>> +	dlb->num_entries++;
> >>>>> +}
> >>>>> +
> >>>>>  /* -----------------------------------------------------------------------------
> >>>>>   * Display List Extended Command Management
> >>>>>   */
> >>>>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> >>>>>  	 * has at least one body, thus we reinitialise the entries list.
> >>>>>  	 */
> >>>>>  	dl->body0->num_entries = 0;
> >>>>> +	dl->body0->num_patches = 0;
> >>>>>  
> >>>>>  	list_add_tail(&dl->list, &dl->dlm->free);
> >>>>>  }
> >>>>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> >>>>>   * Display List Manager
> >>>>>   */
> >>>>>  
> >>>>> +/**
> >>>>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> >>>>> + *	interrupt
> >>>>> + * @dlm: the display list manager
> >>>>> + *
> >>>>> + * Apply all one-shot patches registered for the active display list.
> >>>>> + */
> >>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> >>>>> +{
> >>>>> +	struct vsp1_dl_body *dlb;
> >>>>> +	struct vsp1_dl_list *dl;
> >>>>> +	unsigned int i;
> >>>>> +
> >>>>> +	spin_lock(&dlm->lock);
> >>>>> +
> >>>>> +	dl = dlm->active;
> >>>>> +	if (!dl)
> >>>>> +		goto done;
> >>>>> +
> >>>>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> >>>>> +		for (i = 0; i < dlb->num_patches; ++i)
> >>>>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> >>>>> +		dlb->num_patches = 0;
> >>>>> +	}
> >>>>> +
> >>>>> +done:
> >>>>> +	spin_unlock(&dlm->lock);
> >>>>> +}
> >>>>> +
> >>>> 
> >>>> We've got some HW which doesn't support one-shot writeback, and use a
> >>>> similar trick to try and disable writeback immediately after the flip.
> >>>> 
> >>>> We ran into issues where the "start" interrupt wouldn't run in time to
> >>>> make sure the writeback disable was committed before the next frame.
> >>>> We have to keep track of whether the disable really happened in time,
> >>>> before we release the output buffer.
> >>>> 
> >>>> Might you have a similar problem here?
> >>> 
> >>> We may, but there's no provision at the hardware level to check if the
> >>> configuration updated happened in time. I could add some safety checks
> >>> but I believe they would be racy in the best case :-(
> >> 
> >> We managed to find (what I believe to be...) a non-racy way, but it
> >> will of course depend a lot on the HW behaviour, so I'll leave it to
> >> your best judgement.
> >> 
> >> We basically have a "configuration committed" interrupt which we can
> >> use to set a flag indicating writeback was disabled.
> > 
> > The way my hardware is operated is, roughly, as follows:
> 
> Thanks for the detailed explanation.
> 
> > - The driver prepares a list of values to write to registers and stores
> >   them in DMA-able memory. This doesn't involve the hardware, and so is
> >   completely asynchronous to hardware operation.
> > 
> > - The driver then sets a register with the pointer to the registers
> >   list.
> > 
> > - At the end of the current frame, the hardware reads the memory address
> >   of the registers list and DMAs values to register. This operation is
> >   fast and occurs fully during vertical blanking.
> > 
> > - The hardware then waits for the start of the frame, and begins
> >   processing the framebuffers to send the frame to the display.
> > 
> > - If no new registers list is provided for the next frame, the current
> >   list is reused.
> 
> Does it have to be fetched again, or the HW can use the copy that's in
> registers already? Does it have to fetch a register list every frame
> when the display is active, or you can stop it fetching somehow?

It wouldn't need to be fetched again, but the hardware always fetches
the a list at frame end, regardless of whether a new list is available
or not. I could replace the list with an empty one, but it wouldn't be
very useful.

> > Two interrupts are provided, one at the start of the frame and one at
> > the end of the frame. The driver uses the end of frame interrupt to
> > signal vertical blanking and page flip completion to DRM.
> 
> Just for my understanding - you signal the page flip at the point
> where the HW fetches the register list containing the scene being
> flipped to? Or you signal it after that scene has been on-screen for
> one frame?

I signal the flip when the hardware fetches the new list corresponding
to the frame being flipped to. At that point the addresses of the
previous framebuffers are still in the hardware registers, but they are
in the process of being overwritten with the new ones before the
hardware is started again, so I have a guarantee that the framebuffers
have been effectively flipped out.

> > The end of frame interrupt is also used to schedule atomic commits. The
> > hardware queue depth for atomic commits is just one, so the driver has
> > to wait until the previous commit has been processed before writing the
> > new registers list address to the hardware. To solve the race between
> > the end of frame interrupt and the address write, the hardware provides
> > a status bit that is set to 1 when the address is written, and reset to
> > 0 when the hardware starts processing the registers list.
> > 
> > We thus have three states for an atomic commit:
> > 
> > - active, where the corresponding registers list address has been
> >   written to the hardware, and processed
> > 
> > - queued, where the corresponding registers list address has been
> >   written to the hardware but not processed yet
> > 
> > - pending, where the corresponding registers list address hasn't been
> >   written to the hardware yet
> > 
> > The status bit mentioned above allows us to tell if a list exists in the
> > queued state.
> > 
> > At frame end time, if the status bit is set, we have potentially lost
> > the race between writing the new registers list and the frame end
> > interrupt, so we wait for one more vblank. Otherwise, if a list was
> > queued, we move it to the active state, and retire the active list. If a
> > list was pending, we write its address to the hardware, and move it to
> > the queued state.
> > 
> > To schedule writeback I ended up using the frame start interrupt.
> > Writeback has a specific need in that it requires enabling the memory
> > write interface for a single frame, and that is not something the
> > hardware supports directly. I can't either queue a new registers list
> > with the write interface disabled right after the list with the
> > writeback request became active, as any atomic commit would then be
> > delayed by an extra frame. I thus ended up modifying the registers list
> > in place at frame start time, which is right after the active register
> > list has been DMA'ed to hardware registers. We have the duration of a
> > whole frame to do so, before the hardware transfers the same list again
> > at the beginning of the next frame.
> > 
> > There is a race condition there, as the in-place modification of the
> > active list could be done after the end of the frame if the interrupt
> > latency gets too large. I don't see how I could solve that, as I could
> > write the value after the hardware starts processing the active list at
> > frame end time, but before the interrupt is notified.
> 
> So you can be totally safe by making a copy of the register list,
> disabling writeback there, and signalling writeback completion when
> the HW status bit tells you that new register list has become active.
> The issue is that it effectively halves the maximum update rate when
> writeback is enabled?

Correct. I don't think that's acceptable.

> It sounds like the HW doesn't really give you a way to "cancel" a
> queued register list, which is a bit unfortunate.

I can always queue a new one, but I have no way of telling if the newly
queued list raced with the frame end interrupt.

There's another register I'm not using that contains a shadow copy of
the registers list DMA address. It mirrors the address programmed by the
driver when there is no DMA of the registers list in progress, and
contains the address the of registers list being DMA'ed when a DMA is in
progress. I don't think I can do much with it, as reading it either
before or after reprogramming (to check if I can reprogram or if the
reprogram has been taken into account) is racy.

> You don't happen to have a DMA engine trigger or something you could
> use to do the register list modification at a guaranteed time do you?

Not that I know of, unfortunately.

> Are you always going to be protected by an IOMMU, preventing the
> writeback from trashing physical memory? If that's not the case, then
> the race can have pretty dire consequences.

If the IOMMU is enabled in DT, yes. It's a system-level decision.

> >>> Note that we have the duration of a complete frame to disable writeback,
> >>> as we receive an interrupt when the frame starts, and have until vblank
> >>> to update the configuration. It's thus slightly better than having to
> >>> disable writeback between vblank and the start of the next frame.
> >> 
> >> Yeah... we have a whole frame too. I'm struggling to find our wiki
> >> page with the data, but anecdotally there's some (out-of-tree) drivers
> >> which keep interrupts masked for a _really long_ time. It's nice if
> >> you don't have to care about those :-)
> > 
> > I only provide two options: not my problem, or give me the source code
> > and documentation and let me upstream a clean version of the out-of-tree
> > drivers :-)
> 
> This was a case of drivers "in-the-wild" - I don't know the specifics,
> but they weren't Arm drivers. I was just sharing as a data point.

All those pesky wild drivers that need taming... I've seen my fair share
of monstruosities in that area too. I've even come across with a system
full of hacks that allowed userspace to disable interrupts (I'm not
kidding you).

> >>>>>  /**
> >>>>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> >>>>>   * @dlm: the display list manager
> >>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>> index e0fdb145e6ed..f845607abc4c 100644
> >>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> >>>>>  					unsigned int prealloc);
> >>>>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> >>>>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> >>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> >>>>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> >>>>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> >>>>>  
> >>>>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> >>>>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> >>>>>  
> >>>>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> >>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>>>> +				u32 reset_value);
> >>>>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> >>>>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> >>>>>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-06 18:01             ` Laurent Pinchart
@ 2019-03-07 11:52               ` Liviu Dudau
  2019-03-07 13:48                 ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Liviu Dudau @ 2019-03-07 11:52 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Wed, Mar 06, 2019 at 08:01:53PM +0200, Laurent Pinchart wrote:
> Hi Liviu,
> 
> On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> > On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> > > On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> > >> On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> > >>> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> > >>>> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > >>>>> One-shot entries are used as an alternative to committing a complete new
> > >>>>> display list when a couple of registers need to be written for one frame
> > >>>>> and then reset to another value for all subsequent frames. This will be
> > >>>>> used to implement writeback support that will need to enable writeback
> > >>>>> for the duration of a single frame.
> > >>>>> 
> > >>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > >>>>> ---
> > >>>>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> > >>>>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> > >>>>>  2 files changed, 81 insertions(+)
> > >>>>> 
> > >>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>>>> index 886b3a69d329..7b4d252bfde7 100644
> > >>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > >>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>>>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> > >>>>>  
> > >>>>>  	unsigned int num_entries;
> > >>>>>  	unsigned int max_entries;
> > >>>>> +
> > >>>>> +	unsigned int num_patches;
> > >>>>> +	struct {
> > >>>>> +		struct vsp1_dl_entry *entry;
> > >>>>> +		u32 data;
> > >>>>> +	} patches[2];
> > >>>>>  };
> > >>>>>  
> > >>>>>  /**
> > >>>>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> > >>>>>  		return;
> > >>>>>  
> > >>>>>  	dlb->num_entries = 0;
> > >>>>> +	dlb->num_patches = 0;
> > >>>>>  
> > >>>>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> > >>>>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> > >>>>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> > >>>>>  	dlb->num_entries++;
> > >>>>>  }
> > >>>>>  
> > >>>>> +/**
> > >>>>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > >>>>> + *	single frame
> > >>>>> + * @dlb: The body
> > >>>>> + * @reg: The register address
> > >>>>> + * @value: The register value
> > >>>>> + * @reset_value: The value to reset the register to at the next vblank
> > >>>>> + *
> > >>>>> + * Display lists in continuous mode are re-used by the hardware for successive
> > >>>>> + * frames until a new display list is committed. Changing the VSP configuration
> > >>>>> + * normally requires creating and committing a new display list. This function
> > >>>>> + * offers an alternative race-free way by writing a @value to the @register in
> > >>>>> + * the display list body for a single frame, specifying in @reset_value the
> > >>>>> + * value to reset the register to one vblank after the display list is
> > >>>>> + * committed.
> > >>>>> + *
> > >>>>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> > >>>>> + * and one-shot entries are counted in the total number of entries specified
> > >>>>> + * when the body is allocated by vsp1_dl_body_alloc().
> > >>>>> + */
> > >>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>>>> +				u32 reset_value)
> > >>>>> +{
> > >>>>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > >>>>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> > >>>>> +		return;
> > >>>>> +
> > >>>>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > >>>>> +		      "DLB patches size exceeded (max %zu)",
> > >>>>> +		      ARRAY_SIZE(dlb->patches)))
> > >>>>> +		return;
> > >>>>> +
> > >>>>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > >>>>> +	dlb->patches[dlb->num_patches].data = reset_value;
> > >>>>> +	dlb->num_patches++;
> > >>>>> +
> > >>>>> +	dlb->entries[dlb->num_entries].addr = reg;
> > >>>>> +	dlb->entries[dlb->num_entries].data = value;
> > >>>>> +	dlb->num_entries++;
> > >>>>> +}
> > >>>>> +
> > >>>>>  /* -----------------------------------------------------------------------------
> > >>>>>   * Display List Extended Command Management
> > >>>>>   */
> > >>>>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> > >>>>>  	 * has at least one body, thus we reinitialise the entries list.
> > >>>>>  	 */
> > >>>>>  	dl->body0->num_entries = 0;
> > >>>>> +	dl->body0->num_patches = 0;
> > >>>>>  
> > >>>>>  	list_add_tail(&dl->list, &dl->dlm->free);
> > >>>>>  }
> > >>>>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> > >>>>>   * Display List Manager
> > >>>>>   */
> > >>>>>  
> > >>>>> +/**
> > >>>>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > >>>>> + *	interrupt
> > >>>>> + * @dlm: the display list manager
> > >>>>> + *
> > >>>>> + * Apply all one-shot patches registered for the active display list.
> > >>>>> + */
> > >>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > >>>>> +{
> > >>>>> +	struct vsp1_dl_body *dlb;
> > >>>>> +	struct vsp1_dl_list *dl;
> > >>>>> +	unsigned int i;
> > >>>>> +
> > >>>>> +	spin_lock(&dlm->lock);
> > >>>>> +
> > >>>>> +	dl = dlm->active;
> > >>>>> +	if (!dl)
> > >>>>> +		goto done;
> > >>>>> +
> > >>>>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> > >>>>> +		for (i = 0; i < dlb->num_patches; ++i)
> > >>>>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> > >>>>> +		dlb->num_patches = 0;
> > >>>>> +	}
> > >>>>> +
> > >>>>> +done:
> > >>>>> +	spin_unlock(&dlm->lock);
> > >>>>> +}
> > >>>>> +
> > >>>> 
> > >>>> We've got some HW which doesn't support one-shot writeback, and use a
> > >>>> similar trick to try and disable writeback immediately after the flip.
> > >>>> 
> > >>>> We ran into issues where the "start" interrupt wouldn't run in time to
> > >>>> make sure the writeback disable was committed before the next frame.
> > >>>> We have to keep track of whether the disable really happened in time,
> > >>>> before we release the output buffer.
> > >>>> 
> > >>>> Might you have a similar problem here?
> > >>> 
> > >>> We may, but there's no provision at the hardware level to check if the
> > >>> configuration updated happened in time. I could add some safety checks
> > >>> but I believe they would be racy in the best case :-(
> > >> 
> > >> We managed to find (what I believe to be...) a non-racy way, but it
> > >> will of course depend a lot on the HW behaviour, so I'll leave it to
> > >> your best judgement.
> > >> 
> > >> We basically have a "configuration committed" interrupt which we can
> > >> use to set a flag indicating writeback was disabled.
> > > 
> > > The way my hardware is operated is, roughly, as follows:
> > > 
> > > - The driver prepares a list of values to write to registers and stores
> > >   them in DMA-able memory. This doesn't involve the hardware, and so is
> > >   completely asynchronous to hardware operation.
> > > 
> > > - The driver then sets a register with the pointer to the registers
> > >   list.
> > > 
> > > - At the end of the current frame, the hardware reads the memory address
> > >   of the registers list and DMAs values to register. This operation is
> > >   fast and occurs fully during vertical blanking.
> > > 
> > > - The hardware then waits for the start of the frame, and begins
> > >   processing the framebuffers to send the frame to the display.
> > > 
> > > - If no new registers list is provided for the next frame, the current
> > >   list is reused.
> > 
> > Does this mean the hardware goes back to step 3 and re-reads the memory
> > using DMA?
> 
> That's correct. At frame end the hardware will always read and apply a
> registers list, either a new one or the old one if no new list is
> programmed.
> 
> > > Two interrupts are provided, one at the start of the frame and one at
> > > the end of the frame. The driver uses the end of frame interrupt to
> > > signal vertical blanking and page flip completion to DRM.
> > > 
> > > The end of frame interrupt is also used to schedule atomic commits. The
> > > hardware queue depth for atomic commits is just one, so the driver has
> > > to wait until the previous commit has been processed before writing the
> > > new registers list address to the hardware. To solve the race between
> > > the end of frame interrupt and the address write, the hardware provides
> > > a status bit that is set to 1 when the address is written, and reset to
> > > 0 when the hardware starts processing the registers list.
> > 
> > For my understanding, the following simplication is then true: status = 0
> > means driver is allowed to update the pointer register, status = 1 means
> > currently programmed pointer is used to fetch the register values, right?
> 
> Almost. status = 1 means that the pointer has been programmed by the
> CPU, but not taken into account by the hardware yet. status = 0 means
> that the pointer hasn't been written to by the CPU since the last time
> the hardware read it.

Right, so CPU writes to the pointer register and that sets status to 1,
and when the HW has copied that value into internal DMA registers it clears
it, correct?

> 
> > What happens if you update the pointer while status = 1? DMA transfer gets
> > interrupted and a new one is scheduled? Does the DMA transfer get cancelled?
> 
> You should not do that, as it's racy. The hardware will use either the
> old value of the pointer or new new one, based on the timings of the
> reprogrammation, and you can't tell which one is used. Nothing can
> cancel the DMA, when it's time to start the DMA the hardware will
> transfer the value of the pointer to an internal register and use that
> for DMA. Reprogramming the pointer during the DMA will not affect the
> DMA, the new value will be taken into account for the next DMA.

It looks to me like you can do the in-place update of the writeback
framebuffer address at the end of the current frame (vblank time(?)). If
you wait until status = 0 then you know the next frame parameters are
being "internalised", so you can set in the commit you're about to queue
the disabling of the writeback if that commit doesn't have a fresh
writeback request (phew, that's a mouthful). I don't think you need to
wait until the next start of frame interrupt to do that. Also, vblank
time is probably the time you want to signal the completion of the
previous writeback anyway, right?

> 
> > > We thus have three states for an atomic commit:
> > > 
> > > - active, where the corresponding registers list address has been
> > >   written to the hardware, and processed
> > > 
> > > - queued, where the corresponding registers list address has been
> > >   written to the hardware but not processed yet
> > > 
> > > - pending, where the corresponding registers list address hasn't been
> > >   written to the hardware yet
> > > 
> > > The status bit mentioned above allows us to tell if a list exists in the
> > > queued state.
> > > 
> > > At frame end time, if the status bit is set, we have potentially lost
> > > the race between writing the new registers list and the frame end
> > > interrupt, so we wait for one more vblank. Otherwise, if a list was
> > > queued, we move it to the active state, and retire the active list. If a
> > > list was pending, we write its address to the hardware, and move it to
> > > the queued state.

It looks to me like the moving of the pending state into queued state is your
opportunity to also in-place modify the writeback registers if the state does
not have its own writeback request.

Best regards,
Liviu

> > > 
> > > To schedule writeback I ended up using the frame start interrupt.
> > > Writeback has a specific need in that it requires enabling the memory
> > > write interface for a single frame, and that is not something the
> > > hardware supports directly. I can't either queue a new registers list
> > > with the write interface disabled right after the list with the
> > > writeback request became active, as any atomic commit would then be
> > > delayed by an extra frame. I thus ended up modifying the registers list
> > > in place at frame start time, which is right after the active register
> > > list has been DMA'ed to hardware registers. We have the duration of a
> > > whole frame to do so, before the hardware transfers the same list again
> > > at the beginning of the next frame.
> > 
> > Yes, that is like to what we do for DP500 in mali-dp, where we have a
> > similar situation. HW will continue to stream to the given buffer until
> > stopped, but the writeback API is one-shot mode, so on vblank (when we
> > know the writeback will start at the beginning of the new frame) we
> > program the memwrite to stop, but that only takes effect when we also
> > program the GO bit (CONFIG_VALID in our case).
> > 
> > One problem we had to take care for DP500 was when the next commit (the
> > queued one in your case) also contains a writeback buffer. In that case,
> > we don't stop the writeback but "re-start" it.
> > 
> > > There is a race condition there, as the in-place modification of the
> > > active list could be done after the end of the frame if the interrupt
> > > latency gets too large. I don't see how I could solve that, as I could
> > > write the value after the hardware starts processing the active list at
> > > frame end time, but before the interrupt is notified.
> > 
> > I think that would be a better idea if the HW finishes to transfer
> > before the end of frame interrupt is signalled. Otherwise if your
> > interrupt handler is scheduled too fast, you might overwrite the active
> > frame values?
> 
> That can't happen, as I patch the active registers list in-place in the
> frame start interrupt handler, and frame start occurs after the hardware
> finishes the DMA of the registers list.
> 
> > >>> Note that we have the duration of a complete frame to disable writeback,
> > >>> as we receive an interrupt when the frame starts, and have until vblank
> > >>> to update the configuration. It's thus slightly better than having to
> > >>> disable writeback between vblank and the start of the next frame.
> > >> 
> > >> Yeah... we have a whole frame too. I'm struggling to find our wiki
> > >> page with the data, but anecdotally there's some (out-of-tree) drivers
> > >> which keep interrupts masked for a _really long_ time. It's nice if
> > >> you don't have to care about those :-)
> > > 
> > > I only provide two options: not my problem, or give me the source code
> > > and documentation and let me upstream a clean version of the out-of-tree
> > > drivers :-)
> > > 
> > >>>>>  /**
> > >>>>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> > >>>>>   * @dlm: the display list manager
> > >>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>>>> index e0fdb145e6ed..f845607abc4c 100644
> > >>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > >>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>>>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> > >>>>>  					unsigned int prealloc);
> > >>>>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> > >>>>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > >>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> > >>>>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> > >>>>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> > >>>>>  
> > >>>>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> > >>>>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> > >>>>>  
> > >>>>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > >>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>>>> +				u32 reset_value);
> > >>>>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> > >>>>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> > >>>>>  
> 
> -- 
> Regards,
> 
> Laurent Pinchart

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-06 18:22             ` Laurent Pinchart
@ 2019-03-07 12:28               ` Brian Starkey
  2019-03-08 12:24                 ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-03-07 12:28 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Wed, Mar 06, 2019 at 08:22:44PM +0200, Laurent Pinchart wrote:

[snip]

> 
> I can always queue a new one, but I have no way of telling if the newly
> queued list raced with the frame end interrupt.
> 
> There's another register I'm not using that contains a shadow copy of
> the registers list DMA address. It mirrors the address programmed by the
> driver when there is no DMA of the registers list in progress, and
> contains the address the of registers list being DMA'ed when a DMA is in
> progress. I don't think I can do much with it, as reading it either
> before or after reprogramming (to check if I can reprogram or if the
> reprogram has been taken into account) is racy.
> 

When you say it mirrors the address programmed, is that latched when
the update is accepted, or it will update the moment you program the
address register?

I assume the latter or you would have thought of this yourself (that
seems like a really strange/not-useful behaviour!). But if it is the
former you could:

 - In writeback start-of-frame, create a copy of the register list,
   disabling writeback.
 - Write the address of this copy to the register

If/when an atomic commit comes in before you service the next
end-of-frame:

 - Write the address of the new register list
 - Check the status register. If the "pending" bit is zero, you know
   you had a potential race.
    - Check the DMA address register. If it corresponds to the new
      scene, the new commit won the race, otherwise it's been delayed
      by a frame.

> > You don't happen to have a DMA engine trigger or something you could
> > use to do the register list modification at a guaranteed time do you?
> 
> Not that I know of, unfortunately.
> 
> > Are you always going to be protected by an IOMMU, preventing the
> > writeback from trashing physical memory? If that's not the case, then
> > the race can have pretty dire consequences.
> 
> If the IOMMU is enabled in DT, yes. It's a system-level decision.
> 

Well, it's your driver at the end of the day. But for me, a known
race-condition which would cause random memory corruption sounds like
a really Bad Thing. Halving frame-rate on systems with no IOMMU seems
preferable to me.

Cheers,
-Brian
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-07 11:52               ` Liviu Dudau
@ 2019-03-07 13:48                 ` Laurent Pinchart
  2019-03-07 16:31                   ` Liviu Dudau
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-07 13:48 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Liviu,

On Thu, Mar 07, 2019 at 11:52:18AM +0000, Liviu Dudau wrote:
> On Wed, Mar 06, 2019 at 08:01:53PM +0200, Laurent Pinchart wrote:
> > On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> >> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> >>> On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> >>>> On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> >>>>> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> >>>>>> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> >>>>>>> One-shot entries are used as an alternative to committing a complete new
> >>>>>>> display list when a couple of registers need to be written for one frame
> >>>>>>> and then reset to another value for all subsequent frames. This will be
> >>>>>>> used to implement writeback support that will need to enable writeback
> >>>>>>> for the duration of a single frame.
> >>>>>>> 
> >>>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>>>>>> ---
> >>>>>>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> >>>>>>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> >>>>>>>  2 files changed, 81 insertions(+)
> >>>>>>> 
> >>>>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>>>> index 886b3a69d329..7b4d252bfde7 100644
> >>>>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> >>>>>>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> >>>>>>>  
> >>>>>>>  	unsigned int num_entries;
> >>>>>>>  	unsigned int max_entries;
> >>>>>>> +
> >>>>>>> +	unsigned int num_patches;
> >>>>>>> +	struct {
> >>>>>>> +		struct vsp1_dl_entry *entry;
> >>>>>>> +		u32 data;
> >>>>>>> +	} patches[2];
> >>>>>>>  };
> >>>>>>>  
> >>>>>>>  /**
> >>>>>>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> >>>>>>>  		return;
> >>>>>>>  
> >>>>>>>  	dlb->num_entries = 0;
> >>>>>>> +	dlb->num_patches = 0;
> >>>>>>>  
> >>>>>>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> >>>>>>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> >>>>>>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> >>>>>>>  	dlb->num_entries++;
> >>>>>>>  }
> >>>>>>>  
> >>>>>>> +/**
> >>>>>>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> >>>>>>> + *	single frame
> >>>>>>> + * @dlb: The body
> >>>>>>> + * @reg: The register address
> >>>>>>> + * @value: The register value
> >>>>>>> + * @reset_value: The value to reset the register to at the next vblank
> >>>>>>> + *
> >>>>>>> + * Display lists in continuous mode are re-used by the hardware for successive
> >>>>>>> + * frames until a new display list is committed. Changing the VSP configuration
> >>>>>>> + * normally requires creating and committing a new display list. This function
> >>>>>>> + * offers an alternative race-free way by writing a @value to the @register in
> >>>>>>> + * the display list body for a single frame, specifying in @reset_value the
> >>>>>>> + * value to reset the register to one vblank after the display list is
> >>>>>>> + * committed.
> >>>>>>> + *
> >>>>>>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> >>>>>>> + * and one-shot entries are counted in the total number of entries specified
> >>>>>>> + * when the body is allocated by vsp1_dl_body_alloc().
> >>>>>>> + */
> >>>>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>>>>>> +				u32 reset_value)
> >>>>>>> +{
> >>>>>>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> >>>>>>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> >>>>>>> +		return;
> >>>>>>> +
> >>>>>>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> >>>>>>> +		      "DLB patches size exceeded (max %zu)",
> >>>>>>> +		      ARRAY_SIZE(dlb->patches)))
> >>>>>>> +		return;
> >>>>>>> +
> >>>>>>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> >>>>>>> +	dlb->patches[dlb->num_patches].data = reset_value;
> >>>>>>> +	dlb->num_patches++;
> >>>>>>> +
> >>>>>>> +	dlb->entries[dlb->num_entries].addr = reg;
> >>>>>>> +	dlb->entries[dlb->num_entries].data = value;
> >>>>>>> +	dlb->num_entries++;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>>  /* -----------------------------------------------------------------------------
> >>>>>>>   * Display List Extended Command Management
> >>>>>>>   */
> >>>>>>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> >>>>>>>  	 * has at least one body, thus we reinitialise the entries list.
> >>>>>>>  	 */
> >>>>>>>  	dl->body0->num_entries = 0;
> >>>>>>> +	dl->body0->num_patches = 0;
> >>>>>>>  
> >>>>>>>  	list_add_tail(&dl->list, &dl->dlm->free);
> >>>>>>>  }
> >>>>>>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> >>>>>>>   * Display List Manager
> >>>>>>>   */
> >>>>>>>  
> >>>>>>> +/**
> >>>>>>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> >>>>>>> + *	interrupt
> >>>>>>> + * @dlm: the display list manager
> >>>>>>> + *
> >>>>>>> + * Apply all one-shot patches registered for the active display list.
> >>>>>>> + */
> >>>>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> >>>>>>> +{
> >>>>>>> +	struct vsp1_dl_body *dlb;
> >>>>>>> +	struct vsp1_dl_list *dl;
> >>>>>>> +	unsigned int i;
> >>>>>>> +
> >>>>>>> +	spin_lock(&dlm->lock);
> >>>>>>> +
> >>>>>>> +	dl = dlm->active;
> >>>>>>> +	if (!dl)
> >>>>>>> +		goto done;
> >>>>>>> +
> >>>>>>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> >>>>>>> +		for (i = 0; i < dlb->num_patches; ++i)
> >>>>>>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> >>>>>>> +		dlb->num_patches = 0;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +done:
> >>>>>>> +	spin_unlock(&dlm->lock);
> >>>>>>> +}
> >>>>>>> +
> >>>>>> 
> >>>>>> We've got some HW which doesn't support one-shot writeback, and use a
> >>>>>> similar trick to try and disable writeback immediately after the flip.
> >>>>>> 
> >>>>>> We ran into issues where the "start" interrupt wouldn't run in time to
> >>>>>> make sure the writeback disable was committed before the next frame.
> >>>>>> We have to keep track of whether the disable really happened in time,
> >>>>>> before we release the output buffer.
> >>>>>> 
> >>>>>> Might you have a similar problem here?
> >>>>> 
> >>>>> We may, but there's no provision at the hardware level to check if the
> >>>>> configuration updated happened in time. I could add some safety checks
> >>>>> but I believe they would be racy in the best case :-(
> >>>> 
> >>>> We managed to find (what I believe to be...) a non-racy way, but it
> >>>> will of course depend a lot on the HW behaviour, so I'll leave it to
> >>>> your best judgement.
> >>>> 
> >>>> We basically have a "configuration committed" interrupt which we can
> >>>> use to set a flag indicating writeback was disabled.
> >>> 
> >>> The way my hardware is operated is, roughly, as follows:
> >>> 
> >>> - The driver prepares a list of values to write to registers and stores
> >>>   them in DMA-able memory. This doesn't involve the hardware, and so is
> >>>   completely asynchronous to hardware operation.
> >>> 
> >>> - The driver then sets a register with the pointer to the registers
> >>>   list.
> >>> 
> >>> - At the end of the current frame, the hardware reads the memory address
> >>>   of the registers list and DMAs values to register. This operation is
> >>>   fast and occurs fully during vertical blanking.
> >>> 
> >>> - The hardware then waits for the start of the frame, and begins
> >>>   processing the framebuffers to send the frame to the display.
> >>> 
> >>> - If no new registers list is provided for the next frame, the current
> >>>   list is reused.
> >> 
> >> Does this mean the hardware goes back to step 3 and re-reads the memory
> >> using DMA?
> > 
> > That's correct. At frame end the hardware will always read and apply a
> > registers list, either a new one or the old one if no new list is
> > programmed.
> > 
> >>> Two interrupts are provided, one at the start of the frame and one at
> >>> the end of the frame. The driver uses the end of frame interrupt to
> >>> signal vertical blanking and page flip completion to DRM.
> >>> 
> >>> The end of frame interrupt is also used to schedule atomic commits. The
> >>> hardware queue depth for atomic commits is just one, so the driver has
> >>> to wait until the previous commit has been processed before writing the
> >>> new registers list address to the hardware. To solve the race between
> >>> the end of frame interrupt and the address write, the hardware provides
> >>> a status bit that is set to 1 when the address is written, and reset to
> >>> 0 when the hardware starts processing the registers list.
> >> 
> >> For my understanding, the following simplication is then true: status = 0
> >> means driver is allowed to update the pointer register, status = 1 means
> >> currently programmed pointer is used to fetch the register values, right?
> > 
> > Almost. status = 1 means that the pointer has been programmed by the
> > CPU, but not taken into account by the hardware yet. status = 0 means
> > that the pointer hasn't been written to by the CPU since the last time
> > the hardware read it.
> 
> Right, so CPU writes to the pointer register and that sets status to 1,
> and when the HW has copied that value into internal DMA registers it clears
> it, correct?

Yes, that's correct.

> >> What happens if you update the pointer while status = 1? DMA transfer gets
> >> interrupted and a new one is scheduled? Does the DMA transfer get cancelled?
> > 
> > You should not do that, as it's racy. The hardware will use either the
> > old value of the pointer or new new one, based on the timings of the
> > reprogrammation, and you can't tell which one is used. Nothing can
> > cancel the DMA, when it's time to start the DMA the hardware will
> > transfer the value of the pointer to an internal register and use that
> > for DMA. Reprogramming the pointer during the DMA will not affect the
> > DMA, the new value will be taken into account for the next DMA.
> 
> It looks to me like you can do the in-place update of the writeback
> framebuffer address at the end of the current frame (vblank time(?)). If
> you wait until status = 0 then you know the next frame parameters are
> being "internalised", so you can set in the commit you're about to queue
> the disabling of the writeback if that commit doesn't have a fresh
> writeback request (phew, that's a mouthful).

My problem is that there may not be a next commit, if userspace hasn't
queued one. Otherwise this is what I would do. My initial implementation
was queuing two commits for writeback, one with writeback enabled, and
immediately after a second one copied from the first but with writeback
disabled. This halved the frame rate, as the next commit from userspace
had to first wait for completion of the writeback disabling commit. The
real issue here is that I would need to queue the writeback disabling
commit right after the writeback enabling commit to make sure it gets
processed for the next frame, but once its queued any userspace commit
would need to wait one extra frame as a queued commit can be processed
at any time and thus can't be replaced. That's why I decided to modify
the registers list in place instead of queueing a new commit to disable
writeback.

> I don't think you need to wait until the next start of frame interrupt
> to do that. Also, vblank time is probably the time you want to signal
> the completion of the previous writeback anyway, right?

Correct, that's when I signal it.

> >>> We thus have three states for an atomic commit:
> >>> 
> >>> - active, where the corresponding registers list address has been
> >>>   written to the hardware, and processed
> >>> 
> >>> - queued, where the corresponding registers list address has been
> >>>   written to the hardware but not processed yet
> >>> 
> >>> - pending, where the corresponding registers list address hasn't been
> >>>   written to the hardware yet
> >>> 
> >>> The status bit mentioned above allows us to tell if a list exists in the
> >>> queued state.
> >>> 
> >>> At frame end time, if the status bit is set, we have potentially lost
> >>> the race between writing the new registers list and the frame end
> >>> interrupt, so we wait for one more vblank. Otherwise, if a list was
> >>> queued, we move it to the active state, and retire the active list. If a
> >>> list was pending, we write its address to the hardware, and move it to
> >>> the queued state.
> 
> It looks to me like the moving of the pending state into queued state is your
> opportunity to also in-place modify the writeback registers if the state does
> not have its own writeback request.

Moving from pending to queued means the pointer has been given to the
hardware, but not processed yet. I need to wait until the commit that
enables writeback is fully processed before modifying it in-place to
disable writeback, and that's at the frame start following the move from
the queued state to the active state.

> >>> To schedule writeback I ended up using the frame start interrupt.
> >>> Writeback has a specific need in that it requires enabling the memory
> >>> write interface for a single frame, and that is not something the
> >>> hardware supports directly. I can't either queue a new registers list
> >>> with the write interface disabled right after the list with the
> >>> writeback request became active, as any atomic commit would then be
> >>> delayed by an extra frame. I thus ended up modifying the registers list
> >>> in place at frame start time, which is right after the active register
> >>> list has been DMA'ed to hardware registers. We have the duration of a
> >>> whole frame to do so, before the hardware transfers the same list again
> >>> at the beginning of the next frame.
> >> 
> >> Yes, that is like to what we do for DP500 in mali-dp, where we have a
> >> similar situation. HW will continue to stream to the given buffer until
> >> stopped, but the writeback API is one-shot mode, so on vblank (when we
> >> know the writeback will start at the beginning of the new frame) we
> >> program the memwrite to stop, but that only takes effect when we also
> >> program the GO bit (CONFIG_VALID in our case).
> >> 
> >> One problem we had to take care for DP500 was when the next commit (the
> >> queued one in your case) also contains a writeback buffer. In that case,
> >> we don't stop the writeback but "re-start" it.
> >> 
> >>> There is a race condition there, as the in-place modification of the
> >>> active list could be done after the end of the frame if the interrupt
> >>> latency gets too large. I don't see how I could solve that, as I could
> >>> write the value after the hardware starts processing the active list at
> >>> frame end time, but before the interrupt is notified.
> >> 
> >> I think that would be a better idea if the HW finishes to transfer
> >> before the end of frame interrupt is signalled. Otherwise if your
> >> interrupt handler is scheduled too fast, you might overwrite the active
> >> frame values?
> > 
> > That can't happen, as I patch the active registers list in-place in the
> > frame start interrupt handler, and frame start occurs after the hardware
> > finishes the DMA of the registers list.
> > 
> >>>>> Note that we have the duration of a complete frame to disable writeback,
> >>>>> as we receive an interrupt when the frame starts, and have until vblank
> >>>>> to update the configuration. It's thus slightly better than having to
> >>>>> disable writeback between vblank and the start of the next frame.
> >>>> 
> >>>> Yeah... we have a whole frame too. I'm struggling to find our wiki
> >>>> page with the data, but anecdotally there's some (out-of-tree) drivers
> >>>> which keep interrupts masked for a _really long_ time. It's nice if
> >>>> you don't have to care about those :-)
> >>> 
> >>> I only provide two options: not my problem, or give me the source code
> >>> and documentation and let me upstream a clean version of the out-of-tree
> >>> drivers :-)
> >>> 
> >>>>>>>  /**
> >>>>>>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> >>>>>>>   * @dlm: the display list manager
> >>>>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>>>> index e0fdb145e6ed..f845607abc4c 100644
> >>>>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> >>>>>>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> >>>>>>>  					unsigned int prealloc);
> >>>>>>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> >>>>>>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> >>>>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> >>>>>>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> >>>>>>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> >>>>>>>  
> >>>>>>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> >>>>>>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> >>>>>>>  
> >>>>>>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> >>>>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> >>>>>>> +				u32 reset_value);
> >>>>>>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> >>>>>>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> >>>>>>>  

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-07 13:48                 ` Laurent Pinchart
@ 2019-03-07 16:31                   ` Liviu Dudau
  2019-03-08 12:46                     ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Liviu Dudau @ 2019-03-07 16:31 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Thu, Mar 07, 2019 at 03:48:23PM +0200, Laurent Pinchart wrote:
> Hi Liviu,
> 
> On Thu, Mar 07, 2019 at 11:52:18AM +0000, Liviu Dudau wrote:
> > On Wed, Mar 06, 2019 at 08:01:53PM +0200, Laurent Pinchart wrote:
> > > On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> > >> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> > >>> On Fri, Feb 22, 2019 at 03:06:19PM +0000, Brian Starkey wrote:
> > >>>> On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> > >>>>> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> > >>>>>> On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > >>>>>>> One-shot entries are used as an alternative to committing a complete new
> > >>>>>>> display list when a couple of registers need to be written for one frame
> > >>>>>>> and then reset to another value for all subsequent frames. This will be
> > >>>>>>> used to implement writeback support that will need to enable writeback
> > >>>>>>> for the duration of a single frame.
> > >>>>>>> 
> > >>>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > >>>>>>> ---
> > >>>>>>>  drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> > >>>>>>>  drivers/media/platform/vsp1/vsp1_dl.h |  3 ++
> > >>>>>>>  2 files changed, 81 insertions(+)
> > >>>>>>> 
> > >>>>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>>>>>> index 886b3a69d329..7b4d252bfde7 100644
> > >>>>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > >>>>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > >>>>>>> @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> > >>>>>>>  
> > >>>>>>>  	unsigned int num_entries;
> > >>>>>>>  	unsigned int max_entries;
> > >>>>>>> +
> > >>>>>>> +	unsigned int num_patches;
> > >>>>>>> +	struct {
> > >>>>>>> +		struct vsp1_dl_entry *entry;
> > >>>>>>> +		u32 data;
> > >>>>>>> +	} patches[2];
> > >>>>>>>  };
> > >>>>>>>  
> > >>>>>>>  /**
> > >>>>>>> @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> > >>>>>>>  		return;
> > >>>>>>>  
> > >>>>>>>  	dlb->num_entries = 0;
> > >>>>>>> +	dlb->num_patches = 0;
> > >>>>>>>  
> > >>>>>>>  	spin_lock_irqsave(&dlb->pool->lock, flags);
> > >>>>>>>  	list_add_tail(&dlb->free, &dlb->pool->free);
> > >>>>>>> @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> > >>>>>>>  	dlb->num_entries++;
> > >>>>>>>  }
> > >>>>>>>  
> > >>>>>>> +/**
> > >>>>>>> + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > >>>>>>> + *	single frame
> > >>>>>>> + * @dlb: The body
> > >>>>>>> + * @reg: The register address
> > >>>>>>> + * @value: The register value
> > >>>>>>> + * @reset_value: The value to reset the register to at the next vblank
> > >>>>>>> + *
> > >>>>>>> + * Display lists in continuous mode are re-used by the hardware for successive
> > >>>>>>> + * frames until a new display list is committed. Changing the VSP configuration
> > >>>>>>> + * normally requires creating and committing a new display list. This function
> > >>>>>>> + * offers an alternative race-free way by writing a @value to the @register in
> > >>>>>>> + * the display list body for a single frame, specifying in @reset_value the
> > >>>>>>> + * value to reset the register to one vblank after the display list is
> > >>>>>>> + * committed.
> > >>>>>>> + *
> > >>>>>>> + * The maximum number of one-shot entries is limited to 2 per display list body,
> > >>>>>>> + * and one-shot entries are counted in the total number of entries specified
> > >>>>>>> + * when the body is allocated by vsp1_dl_body_alloc().
> > >>>>>>> + */
> > >>>>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>>>>>> +				u32 reset_value)
> > >>>>>>> +{
> > >>>>>>> +	if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > >>>>>>> +		      "DLB size exceeded (max %u)", dlb->max_entries))
> > >>>>>>> +		return;
> > >>>>>>> +
> > >>>>>>> +	if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > >>>>>>> +		      "DLB patches size exceeded (max %zu)",
> > >>>>>>> +		      ARRAY_SIZE(dlb->patches)))
> > >>>>>>> +		return;
> > >>>>>>> +
> > >>>>>>> +	dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > >>>>>>> +	dlb->patches[dlb->num_patches].data = reset_value;
> > >>>>>>> +	dlb->num_patches++;
> > >>>>>>> +
> > >>>>>>> +	dlb->entries[dlb->num_entries].addr = reg;
> > >>>>>>> +	dlb->entries[dlb->num_entries].data = value;
> > >>>>>>> +	dlb->num_entries++;
> > >>>>>>> +}
> > >>>>>>> +
> > >>>>>>>  /* -----------------------------------------------------------------------------
> > >>>>>>>   * Display List Extended Command Management
> > >>>>>>>   */
> > >>>>>>> @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> > >>>>>>>  	 * has at least one body, thus we reinitialise the entries list.
> > >>>>>>>  	 */
> > >>>>>>>  	dl->body0->num_entries = 0;
> > >>>>>>> +	dl->body0->num_patches = 0;
> > >>>>>>>  
> > >>>>>>>  	list_add_tail(&dl->list, &dl->dlm->free);
> > >>>>>>>  }
> > >>>>>>> @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> > >>>>>>>   * Display List Manager
> > >>>>>>>   */
> > >>>>>>>  
> > >>>>>>> +/**
> > >>>>>>> + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > >>>>>>> + *	interrupt
> > >>>>>>> + * @dlm: the display list manager
> > >>>>>>> + *
> > >>>>>>> + * Apply all one-shot patches registered for the active display list.
> > >>>>>>> + */
> > >>>>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > >>>>>>> +{
> > >>>>>>> +	struct vsp1_dl_body *dlb;
> > >>>>>>> +	struct vsp1_dl_list *dl;
> > >>>>>>> +	unsigned int i;
> > >>>>>>> +
> > >>>>>>> +	spin_lock(&dlm->lock);
> > >>>>>>> +
> > >>>>>>> +	dl = dlm->active;
> > >>>>>>> +	if (!dl)
> > >>>>>>> +		goto done;
> > >>>>>>> +
> > >>>>>>> +	list_for_each_entry(dlb, &dl->bodies, list) {
> > >>>>>>> +		for (i = 0; i < dlb->num_patches; ++i)
> > >>>>>>> +			dlb->patches[i].entry->data = dlb->patches[i].data;
> > >>>>>>> +		dlb->num_patches = 0;
> > >>>>>>> +	}
> > >>>>>>> +
> > >>>>>>> +done:
> > >>>>>>> +	spin_unlock(&dlm->lock);
> > >>>>>>> +}
> > >>>>>>> +
> > >>>>>> 
> > >>>>>> We've got some HW which doesn't support one-shot writeback, and use a
> > >>>>>> similar trick to try and disable writeback immediately after the flip.
> > >>>>>> 
> > >>>>>> We ran into issues where the "start" interrupt wouldn't run in time to
> > >>>>>> make sure the writeback disable was committed before the next frame.
> > >>>>>> We have to keep track of whether the disable really happened in time,
> > >>>>>> before we release the output buffer.
> > >>>>>> 
> > >>>>>> Might you have a similar problem here?
> > >>>>> 
> > >>>>> We may, but there's no provision at the hardware level to check if the
> > >>>>> configuration updated happened in time. I could add some safety checks
> > >>>>> but I believe they would be racy in the best case :-(
> > >>>> 
> > >>>> We managed to find (what I believe to be...) a non-racy way, but it
> > >>>> will of course depend a lot on the HW behaviour, so I'll leave it to
> > >>>> your best judgement.
> > >>>> 
> > >>>> We basically have a "configuration committed" interrupt which we can
> > >>>> use to set a flag indicating writeback was disabled.
> > >>> 
> > >>> The way my hardware is operated is, roughly, as follows:
> > >>> 
> > >>> - The driver prepares a list of values to write to registers and stores
> > >>>   them in DMA-able memory. This doesn't involve the hardware, and so is
> > >>>   completely asynchronous to hardware operation.
> > >>> 
> > >>> - The driver then sets a register with the pointer to the registers
> > >>>   list.
> > >>> 
> > >>> - At the end of the current frame, the hardware reads the memory address
> > >>>   of the registers list and DMAs values to register. This operation is
> > >>>   fast and occurs fully during vertical blanking.
> > >>> 
> > >>> - The hardware then waits for the start of the frame, and begins
> > >>>   processing the framebuffers to send the frame to the display.
> > >>> 
> > >>> - If no new registers list is provided for the next frame, the current
> > >>>   list is reused.
> > >> 
> > >> Does this mean the hardware goes back to step 3 and re-reads the memory
> > >> using DMA?
> > > 
> > > That's correct. At frame end the hardware will always read and apply a
> > > registers list, either a new one or the old one if no new list is
> > > programmed.
> > > 
> > >>> Two interrupts are provided, one at the start of the frame and one at
> > >>> the end of the frame. The driver uses the end of frame interrupt to
> > >>> signal vertical blanking and page flip completion to DRM.
> > >>> 
> > >>> The end of frame interrupt is also used to schedule atomic commits. The
> > >>> hardware queue depth for atomic commits is just one, so the driver has
> > >>> to wait until the previous commit has been processed before writing the
> > >>> new registers list address to the hardware. To solve the race between
> > >>> the end of frame interrupt and the address write, the hardware provides
> > >>> a status bit that is set to 1 when the address is written, and reset to
> > >>> 0 when the hardware starts processing the registers list.
> > >> 
> > >> For my understanding, the following simplication is then true: status = 0
> > >> means driver is allowed to update the pointer register, status = 1 means
> > >> currently programmed pointer is used to fetch the register values, right?
> > > 
> > > Almost. status = 1 means that the pointer has been programmed by the
> > > CPU, but not taken into account by the hardware yet. status = 0 means
> > > that the pointer hasn't been written to by the CPU since the last time
> > > the hardware read it.
> > 
> > Right, so CPU writes to the pointer register and that sets status to 1,
> > and when the HW has copied that value into internal DMA registers it clears
> > it, correct?
> 
> Yes, that's correct.
> 
> > >> What happens if you update the pointer while status = 1? DMA transfer gets
> > >> interrupted and a new one is scheduled? Does the DMA transfer get cancelled?
> > > 
> > > You should not do that, as it's racy. The hardware will use either the
> > > old value of the pointer or new new one, based on the timings of the
> > > reprogrammation, and you can't tell which one is used. Nothing can
> > > cancel the DMA, when it's time to start the DMA the hardware will
> > > transfer the value of the pointer to an internal register and use that
> > > for DMA. Reprogramming the pointer during the DMA will not affect the
> > > DMA, the new value will be taken into account for the next DMA.
> > 
> > It looks to me like you can do the in-place update of the writeback
> > framebuffer address at the end of the current frame (vblank time(?)). If
> > you wait until status = 0 then you know the next frame parameters are
> > being "internalised", so you can set in the commit you're about to queue
> > the disabling of the writeback if that commit doesn't have a fresh
> > writeback request (phew, that's a mouthful).
> 
> My problem is that there may not be a next commit, if userspace hasn't
> queued one. Otherwise this is what I would do. My initial implementation
> was queuing two commits for writeback, one with writeback enabled, and
> immediately after a second one copied from the first but with writeback
> disabled. This halved the frame rate, as the next commit from userspace
> had to first wait for completion of the writeback disabling commit. The
> real issue here is that I would need to queue the writeback disabling
> commit right after the writeback enabling commit to make sure it gets
> processed for the next frame, but once its queued any userspace commit
> would need to wait one extra frame as a queued commit can be processed
> at any time and thus can't be replaced. That's why I decided to modify
> the registers list in place instead of queueing a new commit to disable
> writeback.
> 
> > I don't think you need to wait until the next start of frame interrupt
> > to do that. Also, vblank time is probably the time you want to signal
> > the completion of the previous writeback anyway, right?
> 
> Correct, that's when I signal it.
> 
> > >>> We thus have three states for an atomic commit:
> > >>> 
> > >>> - active, where the corresponding registers list address has been
> > >>>   written to the hardware, and processed
> > >>> 
> > >>> - queued, where the corresponding registers list address has been
> > >>>   written to the hardware but not processed yet
> > >>> 
> > >>> - pending, where the corresponding registers list address hasn't been
> > >>>   written to the hardware yet
> > >>> 
> > >>> The status bit mentioned above allows us to tell if a list exists in the
> > >>> queued state.
> > >>> 
> > >>> At frame end time, if the status bit is set, we have potentially lost
> > >>> the race between writing the new registers list and the frame end
> > >>> interrupt, so we wait for one more vblank. Otherwise, if a list was
> > >>> queued, we move it to the active state, and retire the active list. If a
> > >>> list was pending, we write its address to the hardware, and move it to
> > >>> the queued state.
> > 
> > It looks to me like the moving of the pending state into queued state is your
> > opportunity to also in-place modify the writeback registers if the state does
> > not have its own writeback request.
> 
> Moving from pending to queued means the pointer has been given to the
> hardware, but not processed yet. I need to wait until the commit that
> enables writeback is fully processed before modifying it in-place to
> disable writeback, and that's at the frame start following the move from
> the queued state to the active state.

I'm not attempting to (re)write your driver, only to explain my thinking process in
a way that is easiest for me:

1. driver prepares a new commit that might have a writeback and sets the
pointer register to the new address. It then marks the commit as queued.

(optional) 2. driver receives a new commit that is marked as pending

3. end-of-frame interrupt arrives
     a. HW reads the new address and programs a DMA transfer to update registers.
     b. driver reads the status bit and waits until status == 0.
     c. driver marks queued commit as active and looks if it has any pending commits
          - if yes, it looks at the pending commit if it has a writeback request
	        - if no, it updates the pending commit to write the register(s) that disable writeback
		- moves pending commit to queued
	  - if no, then it copies active commit and disables writeback. (*)
     d. driver signals vblank and any previous writebacks that might have been programmed
        by the previously active commit

4. ....
5. profit!


(*) depending on how the HW behaves, it might be enough to create a stub
    commit that only disables the writeback, with no other update.

This way writeback commits will always be followed by another commit, even if it is a dummy one
that only disables writeback.

Best regards,
Liviu

> 
> > >>> To schedule writeback I ended up using the frame start interrupt.
> > >>> Writeback has a specific need in that it requires enabling the memory
> > >>> write interface for a single frame, and that is not something the
> > >>> hardware supports directly. I can't either queue a new registers list
> > >>> with the write interface disabled right after the list with the
> > >>> writeback request became active, as any atomic commit would then be
> > >>> delayed by an extra frame. I thus ended up modifying the registers list
> > >>> in place at frame start time, which is right after the active register
> > >>> list has been DMA'ed to hardware registers. We have the duration of a
> > >>> whole frame to do so, before the hardware transfers the same list again
> > >>> at the beginning of the next frame.
> > >> 
> > >> Yes, that is like to what we do for DP500 in mali-dp, where we have a
> > >> similar situation. HW will continue to stream to the given buffer until
> > >> stopped, but the writeback API is one-shot mode, so on vblank (when we
> > >> know the writeback will start at the beginning of the new frame) we
> > >> program the memwrite to stop, but that only takes effect when we also
> > >> program the GO bit (CONFIG_VALID in our case).
> > >> 
> > >> One problem we had to take care for DP500 was when the next commit (the
> > >> queued one in your case) also contains a writeback buffer. In that case,
> > >> we don't stop the writeback but "re-start" it.
> > >> 
> > >>> There is a race condition there, as the in-place modification of the
> > >>> active list could be done after the end of the frame if the interrupt
> > >>> latency gets too large. I don't see how I could solve that, as I could
> > >>> write the value after the hardware starts processing the active list at
> > >>> frame end time, but before the interrupt is notified.
> > >> 
> > >> I think that would be a better idea if the HW finishes to transfer
> > >> before the end of frame interrupt is signalled. Otherwise if your
> > >> interrupt handler is scheduled too fast, you might overwrite the active
> > >> frame values?
> > > 
> > > That can't happen, as I patch the active registers list in-place in the
> > > frame start interrupt handler, and frame start occurs after the hardware
> > > finishes the DMA of the registers list.
> > > 
> > >>>>> Note that we have the duration of a complete frame to disable writeback,
> > >>>>> as we receive an interrupt when the frame starts, and have until vblank
> > >>>>> to update the configuration. It's thus slightly better than having to
> > >>>>> disable writeback between vblank and the start of the next frame.
> > >>>> 
> > >>>> Yeah... we have a whole frame too. I'm struggling to find our wiki
> > >>>> page with the data, but anecdotally there's some (out-of-tree) drivers
> > >>>> which keep interrupts masked for a _really long_ time. It's nice if
> > >>>> you don't have to care about those :-)
> > >>> 
> > >>> I only provide two options: not my problem, or give me the source code
> > >>> and documentation and let me upstream a clean version of the out-of-tree
> > >>> drivers :-)
> > >>> 
> > >>>>>>>  /**
> > >>>>>>>   * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> > >>>>>>>   * @dlm: the display list manager
> > >>>>>>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>>>>>> index e0fdb145e6ed..f845607abc4c 100644
> > >>>>>>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > >>>>>>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > >>>>>>> @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> > >>>>>>>  					unsigned int prealloc);
> > >>>>>>>  void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> > >>>>>>>  void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > >>>>>>> +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> > >>>>>>>  unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> > >>>>>>>  struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> > >>>>>>>  
> > >>>>>>> @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> > >>>>>>>  void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> > >>>>>>>  
> > >>>>>>>  void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > >>>>>>> +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > >>>>>>> +				u32 reset_value);
> > >>>>>>>  int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> > >>>>>>>  int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> > >>>>>>>  
> 
> -- 
> Regards,
> 
> Laurent Pinchart

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-07 12:28               ` Brian Starkey
@ 2019-03-08 12:24                 ` Laurent Pinchart
  2019-03-18 16:59                   ` Brian Starkey
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-08 12:24 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Thu, Mar 07, 2019 at 12:28:08PM +0000, Brian Starkey wrote:
> On Wed, Mar 06, 2019 at 08:22:44PM +0200, Laurent Pinchart wrote:
> 
> [snip]
> 
> > I can always queue a new one, but I have no way of telling if the newly
> > queued list raced with the frame end interrupt.
> > 
> > There's another register I'm not using that contains a shadow copy of
> > the registers list DMA address. It mirrors the address programmed by the
> > driver when there is no DMA of the registers list in progress, and
> > contains the address the of registers list being DMA'ed when a DMA is in
> > progress. I don't think I can do much with it, as reading it either
> > before or after reprogramming (to check if I can reprogram or if the
> > reprogram has been taken into account) is racy.
> 
> When you say it mirrors the address programmed, is that latched when
> the update is accepted, or it will update the moment you program the
> address register?

It is latched when the update is processed, at the next vblank following
the address programming. The timing diagram below shows vblank, the UPD
bit I use for synchronization, the registers list address register
exposed to the CPU, and the internal address used to DMA the registers
list. The address register is written four times to show how the
hardware reacts, but in practice there will be a single write per frame,
right after the beginning of vblank.

                                      DMA starts
                                      |     DMA ends
                                      |     |
                                      V     V
                                      ___________
VBLANK         ______________________|           |________
                     ________________     ________________
UPD            _____|                |___|
               _____ ______ _____________ ________ _______
ADDR register  __A__X__B___X______C______X___D____X___E___
               ______________________ ____________________
ADDR internal  ___________A__________X__________C_________

I can reprogram the address any number of times I want before the
vblank, but the update bit mechanism only lets me protect the race
related to the first write. For any subsequent write I won't be able to
tell whether it was complete before the hardware started the frame, or
arrived too late.

> I assume the latter or you would have thought of this yourself (that
> seems like a really strange/not-useful behaviour!). But if it is the
> former you could:
> 
>  - In writeback start-of-frame, create a copy of the register list,
>    disabling writeback.
>  - Write the address of this copy to the register
> 
> If/when an atomic commit comes in before you service the next
> end-of-frame:
> 
>  - Write the address of the new register list
>  - Check the status register. If the "pending" bit is zero, you know
>    you had a potential race.
>     - Check the DMA address register. If it corresponds to the new
>       scene, the new commit won the race, otherwise it's been delayed
>       by a frame.

The issue here is that there's a potential race if the pending (UPD) bit
is one too. If the commit arrives just before vblank, but the address is
written just after vblank, after the UPD bit has been reset to 0 by the
hardware, but before the vblank interrupt is processed, then the commit
won't be applied yet, and I will have no way to know. Compare the two
following scenarios:

		   [1]       [2] [3]           [4]       [5]
                    |         |   |             |         |
                    |         V   V             |         V
                    V          __________       V          __________
VBLANK         _______________|          |________________|          |__
                     _________     _______________________
UPD            _____|         |___|                       |_____________
               _____ _____________ _____________ _______________________
ADDR register  __A__X______B______X______C______X___________D___________
               ___________________________________________ _____________
ADDR internal  _______A_______X______B____________________X______D______

[1] Atomic commit, registers list address write, with writeback enabled
[2] DMA starts for first atomic commit
[3] Writeback disable registers list address write
[4] Next atomic commit, with writeback disabled
[5] DMA starts for second atomic commit

		   [1]       [2] [3]                     [4][5]
                    |         |   |                       |  |
                    |         V   V                       V  V
                    V          __________                  __________
VBLANK         _______________|          |________________|          |__
                     _________     _______________________    __________
UPD            _____|         |___|                       |__|
               _____ _____________ __________________________ __________
ADDR register  __A__X______B______X_____________C____________X_____D____
               ___________________________________________ _____________
ADDR internal  _______A_______X______B____________________X______C______

[1] Atomic commit, registers list address write, with writeback enabled
[2] DMA starts for first atomic commit
[3] Writeback disable registers list address write
[4] DMA starts for writeback disable registers list (3)
[5] Next atomic commit, with writeback disabled, performed right after
    vblank but befrore the vblank interrupt is serviced

The UPD bit is 1 after writing the ADDR register the second time in both
cases. Furthermore, if [4] and [5] are very close in the second case,
the UPD bit may read 1 just before [5] if the read comes before [4]:

	read UPD bit;
	/* VBLANK [4] */
	write ADDR register;

I thus can't rely on UPD = 1 before the write meaning that the write was
performed before vblank, and I can't rely either on the UPD bit after
write, as it's 1 in both cases.

I initially thought I could protect against the race using the following
procedure.

- Single atomic commit, no IRQ delay

		   [1]       [2]        [3]              [4]
                    |         |          |                |
                    |         V          V                V
                    V          __________                  __________
VBLANK         _______________|          |________________|          |__
                     _________    
UPD            _____|         |_________________________________________
               _____ ___________________________________________________
ADDR register  __A__X___________________B_______________________________
               _______________ _________________________________________
ADDR internal  _______A_______X______B__________________________________

[1] Atomic commit, registers list address write, with writeback enabled
[2] DMA starts for first atomic commit
[3] Frame start, disable writeback in-place in registers list B
[4] DMA starts for "patched" registers list, disables writeback

- Two atomic commits, no IRQ delay

		   [1]       [2]  [3]   [4]              [5]
                    |         |    |     |                |
                    |         V    V     V                V
                    V          __________                  __________
VBLANK         _______________|          |________________|          |__
                     _________      ______________________
UPD            _____|         |____|                      |_____________
               _____ ______________ ____________________________________
ADDR register  __A__X______B_______X_________________C__________________
               _______________ ___________________________ _____________
ADDR internal  _______A_______X_____________B_____________X______C______

[1] Atomic commit, registers list address write, with writeback enabled
[2] DMA starts for first atomic commit
[3] Next atomic commit, registers list address write, with writeback
    disabled
[4] Frame start, disable writeback in-place in registers list B
[5] DMA starts for second atomic commit, disables writeback

[3] and [4] can happen in any order, as long as they both come before
[5]. If [3] comes after [5], we're back to the previous case (Single
atomic commit, no IRQ delay).

- Single atomic commit, IRQ delay

		   [1]       [2]        [3]              [4] [5]
                    |         |          |                |   |
                    |         V          V                V   V
                    V          __________                  __________
VBLANK         _______________|          |________________|          |__
                     _________    
UPD            _____|         |_________________________________________
               _____ ___________________________________________________
ADDR register  __A__X___________________B_______________________________
               _______________ _________________________________________
ADDR internal  _______A_______X______B__________________________________

[1] Atomic commit, registers list address write, with writeback enabled
[2] DMA starts for first atomic commit
[3] Frame start, IRQ is delayed to [5]
[4] DMA starts for unmodified registers list, writeback still enable
[5] disable writeback in-place in registers list B, too late

Here I need to detect that [5] was delayed after [4], and thus delay the
completion of the writeback job by one frame. This could be done by
checking the vblank interrupt status bit, if it is set then vblank
occurred and raced [5]. However, the same issue can also happen when no
race occurred if processing of the vblank interrupt for [2] is delayed
until [3]. Both the vblank interrupt and the frame start interrupt
status bits will be set, indicate a potential race.

The time between [2] and [3] is very short compared to the time between
[3] and [4] and to interrupt latency in general, so we would have lots
of false positives.

> >> You don't happen to have a DMA engine trigger or something you could
> >> use to do the register list modification at a guaranteed time do you?
> > 
> > Not that I know of, unfortunately.
> > 
> >> Are you always going to be protected by an IOMMU, preventing the
> >> writeback from trashing physical memory? If that's not the case, then
> >> the race can have pretty dire consequences.
> > 
> > If the IOMMU is enabled in DT, yes. It's a system-level decision.
> 
> Well, it's your driver at the end of the day. But for me, a known
> race-condition which would cause random memory corruption sounds like
> a really Bad Thing. Halving frame-rate on systems with no IOMMU seems
> preferable to me.

It is a really bad thing. I think the decision should be taken by
Renesas though.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-07 16:31                   ` Liviu Dudau
@ 2019-03-08 12:46                     ` Laurent Pinchart
  2019-03-08 15:02                       ` Liviu Dudau
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-08 12:46 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Liviu,

On Thu, Mar 07, 2019 at 04:31:40PM +0000, Liviu Dudau wrote:
> On Thu, Mar 07, 2019 at 03:48:23PM +0200, Laurent Pinchart wrote:
> > On Thu, Mar 07, 2019 at 11:52:18AM +0000, Liviu Dudau wrote:
> >> On Wed, Mar 06, 2019 at 08:01:53PM +0200, Laurent Pinchart wrote:
> >>> On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> >>>> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:

[snip]

> >>>>> We thus have three states for an atomic commit:
> >>>>> 
> >>>>> - active, where the corresponding registers list address has been
> >>>>>   written to the hardware, and processed
> >>>>> 
> >>>>> - queued, where the corresponding registers list address has been
> >>>>>   written to the hardware but not processed yet
> >>>>> 
> >>>>> - pending, where the corresponding registers list address hasn't been
> >>>>>   written to the hardware yet
> >>>>> 
> >>>>> The status bit mentioned above allows us to tell if a list exists in the
> >>>>> queued state.
> >>>>> 
> >>>>> At frame end time, if the status bit is set, we have potentially lost
> >>>>> the race between writing the new registers list and the frame end
> >>>>> interrupt, so we wait for one more vblank. Otherwise, if a list was
> >>>>> queued, we move it to the active state, and retire the active list. If a
> >>>>> list was pending, we write its address to the hardware, and move it to
> >>>>> the queued state.
> >> 
> >> It looks to me like the moving of the pending state into queued state is your
> >> opportunity to also in-place modify the writeback registers if the state does
> >> not have its own writeback request.
> > 
> > Moving from pending to queued means the pointer has been given to the
> > hardware, but not processed yet. I need to wait until the commit that
> > enables writeback is fully processed before modifying it in-place to
> > disable writeback, and that's at the frame start following the move from
> > the queued state to the active state.
> 
> I'm not attempting to (re)write your driver, only to explain my thinking process in
> a way that is easiest for me:

I wouldn't mind if you attempted to rewrite the driver if it ended up in
a better state :-)

> 1. driver prepares a new commit that might have a writeback and sets the
> pointer register to the new address. It then marks the commit as queued.
> 
> (optional) 2. driver receives a new commit that is marked as pending
> 
> 3. end-of-frame interrupt arrives
>      a. HW reads the new address and programs a DMA transfer to update registers.
>      b. driver reads the status bit and waits until status == 0.

Step b is not needed as the status bit is set to 0 as soon as the
hardware starts the DMA.

>      c. driver marks queued commit as active and looks if it has any pending commits
>           - if yes, it looks at the pending commit if it has a writeback request
> 	        - if no, it updates the pending commit to write the register(s) that disable writeback
> 		- moves pending commit to queued
> 	  - if no, then it copies active commit and disables writeback. (*)

Pending commits are very rare. Userspace will usually queue the next
commit when it receives notification of completion of the previous
commit, so the next commit will arrive after vblank, with a single
commit per frame interval. At that point there will be an active commit,
and no queued commit, and the new commit will become the queued commit.
The "no" case will thus be hit in the vast majority of cases, preventing
the next userspace commit to be queued for the same frame, delaying it
by one frame, and thus halving the frame rate :-(

>      d. driver signals vblank and any previous writebacks that might have been programmed
>         by the previously active commit
> 
> 4. ....
> 5. profit!
> 
> 
> (*) depending on how the HW behaves, it might be enough to create a stub
>     commit that only disables the writeback, with no other update.

A stub is enough, there's no need to reprogram everything.

> This way writeback commits will always be followed by another commit,
> even if it is a dummy one that only disables writeback.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-08 12:46                     ` Laurent Pinchart
@ 2019-03-08 15:02                       ` Liviu Dudau
  2019-03-13  0:56                         ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Liviu Dudau @ 2019-03-08 15:02 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

On Fri, Mar 08, 2019 at 02:46:08PM +0200, Laurent Pinchart wrote:
> Hi Liviu,
> 
> On Thu, Mar 07, 2019 at 04:31:40PM +0000, Liviu Dudau wrote:
> > On Thu, Mar 07, 2019 at 03:48:23PM +0200, Laurent Pinchart wrote:
> > > On Thu, Mar 07, 2019 at 11:52:18AM +0000, Liviu Dudau wrote:
> > >> On Wed, Mar 06, 2019 at 08:01:53PM +0200, Laurent Pinchart wrote:
> > >>> On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> > >>>> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> 
> [snip]
> 
> > >>>>> We thus have three states for an atomic commit:
> > >>>>> 
> > >>>>> - active, where the corresponding registers list address has been
> > >>>>>   written to the hardware, and processed
> > >>>>> 
> > >>>>> - queued, where the corresponding registers list address has been
> > >>>>>   written to the hardware but not processed yet
> > >>>>> 
> > >>>>> - pending, where the corresponding registers list address hasn't been
> > >>>>>   written to the hardware yet
> > >>>>> 
> > >>>>> The status bit mentioned above allows us to tell if a list exists in the
> > >>>>> queued state.
> > >>>>> 
> > >>>>> At frame end time, if the status bit is set, we have potentially lost
> > >>>>> the race between writing the new registers list and the frame end
> > >>>>> interrupt, so we wait for one more vblank. Otherwise, if a list was
> > >>>>> queued, we move it to the active state, and retire the active list. If a
> > >>>>> list was pending, we write its address to the hardware, and move it to
> > >>>>> the queued state.
> > >> 
> > >> It looks to me like the moving of the pending state into queued state is your
> > >> opportunity to also in-place modify the writeback registers if the state does
> > >> not have its own writeback request.
> > > 
> > > Moving from pending to queued means the pointer has been given to the
> > > hardware, but not processed yet. I need to wait until the commit that
> > > enables writeback is fully processed before modifying it in-place to
> > > disable writeback, and that's at the frame start following the move from
> > > the queued state to the active state.
> > 
> > I'm not attempting to (re)write your driver, only to explain my thinking process in
> > a way that is easiest for me:
> 
> I wouldn't mind if you attempted to rewrite the driver if it ended up in
> a better state :-)
> 
> > 1. driver prepares a new commit that might have a writeback and sets the
> > pointer register to the new address. It then marks the commit as queued.
> > 
> > (optional) 2. driver receives a new commit that is marked as pending
> > 
> > 3. end-of-frame interrupt arrives
> >      a. HW reads the new address and programs a DMA transfer to update registers.
> >      b. driver reads the status bit and waits until status == 0.
> 
> Step b is not needed as the status bit is set to 0 as soon as the
> hardware starts the DMA.

OK, however because I'm not sure when the driver's interrupt handler is called,
I was trying to be safe and make sure the driver can proceed.

> 
> >      c. driver marks queued commit as active and looks if it has any pending commits
> >           - if yes, it looks at the pending commit if it has a writeback request
> > 	        - if no, it updates the pending commit to write the register(s) that disable writeback
> > 		- moves pending commit to queued
> > 	  - if no, then it copies active commit and disables writeback. (*)
> 
> Pending commits are very rare. Userspace will usually queue the next
> commit when it receives notification of completion of the previous
> commit, so the next commit will arrive after vblank, with a single
> commit per frame interval. At that point there will be an active commit,
> and no queued commit, and the new commit will become the queued commit.
> The "no" case will thus be hit in the vast majority of cases, preventing
> the next userspace commit to be queued for the same frame, delaying it
> by one frame, and thus halving the frame rate :-(

I'll guess you're referring to the (*) case of "no", but I'm not sure how the next
userspace commit will be delayed ..... wait a bit .... a light bulb turned on
somewhere in the darkness of my mind .... see below.

> 
> >      d. driver signals vblank and any previous writebacks that might have been programmed
> >         by the previously active commit
> > 
> > 4. ....
> > 5. profit!
> > 
> > 
> > (*) depending on how the HW behaves, it might be enough to create a stub
> >     commit that only disables the writeback, with no other update.
> 
> A stub is enough, there's no need to reprogram everything.

Right, in that case, can you not look at the queued commit when the next userspace
commit comes in and if it is the stub commit overwrite it with your new commit as
if you're at step c. ?

Best regards,
Liviu

> 
> > This way writeback commits will always be followed by another commit,
> > even if it is a dummy one that only disables writeback.
> 
> -- 
> Regards,
> 
> Laurent Pinchart

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation
  2019-02-21 10:32 ` [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation Laurent Pinchart
@ 2019-03-11 22:57   ` Kieran Bingham
  2019-03-12 15:24     ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Kieran Bingham @ 2019-03-11 22:57 UTC (permalink / raw)
  To: Laurent Pinchart, dri-devel; +Cc: Liviu Dudau, James Qian Wang

Hi Laurent,

On 21/02/2019 10:32, Laurent Pinchart wrote:
> The rcar_du_crtc structure index field contains the CRTC hardware index,
> not the hardware and software index. Update the documentation
> accordingly.

Should this have a fixes tag? It's only trivial - but if so:


Fixes: 5361cc7f8e91 ("drm: rcar-du: Split CRTC handling to support
hardware indexing")

Either way,

Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
> index bcb35b0b7612..c478953be092 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
> @@ -27,7 +27,7 @@ struct rcar_du_vsp;
>   * @clock: the CRTC functional clock
>   * @extclock: external pixel dot clock (optional)
>   * @mmio_offset: offset of the CRTC registers in the DU MMIO block
> - * @index: CRTC software and hardware index
> + * @index: CRTC hardware index
>   * @initialized: whether the CRTC has been initialized and clocks enabled
>   * @dsysr: cached value of the DSYSR register
>   * @vblank_enable: whether vblank events are enabled on this CRTC
> 

-- 
Regards
--
Kieran
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 17/19] drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure
  2019-02-21 10:32 ` [PATCH v5 17/19] drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure Laurent Pinchart
@ 2019-03-11 23:20   ` Kieran Bingham
  0 siblings, 0 replies; 65+ messages in thread
From: Kieran Bingham @ 2019-03-11 23:20 UTC (permalink / raw)
  To: Laurent Pinchart, dri-devel; +Cc: Liviu Dudau, James Qian Wang

Hi Laurent,

On 21/02/2019 10:32, Laurent Pinchart wrote:
> The mapping between DRM and V4L2 fourcc's is stored in two separate
> tables in rcar_du_vsp.c. In order to make it reusable to implement
> writeback support, move it to the rcar_du_format_info structure.


It's a shame there isn't some core framework conversion helpers that do
these mappings (presumably in both directions). But even if we had one
of those, having a table entry here is a fast conversion.


Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>


> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/gpu/drm/rcar-du/rcar_du_kms.c | 25 +++++++++++++++
>  drivers/gpu/drm/rcar-du/rcar_du_kms.h |  1 +
>  drivers/gpu/drm/rcar-du/rcar_du_vsp.c | 44 ++++-----------------------
>  3 files changed, 32 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> index b0c80dffd8b8..999440c7b258 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -32,60 +32,70 @@
>  static const struct rcar_du_format_info rcar_du_format_infos[] = {
>  	{
>  		.fourcc = DRM_FORMAT_RGB565,
> +		.v4l2 = V4L2_PIX_FMT_RGB565,
>  		.bpp = 16,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_ARGB1555,
> +		.v4l2 = V4L2_PIX_FMT_ARGB555,
>  		.bpp = 16,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_XRGB1555,
> +		.v4l2 = V4L2_PIX_FMT_XRGB555,
>  		.bpp = 16,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_XRGB8888,
> +		.v4l2 = V4L2_PIX_FMT_XBGR32,
>  		.bpp = 32,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
>  		.edf = PnDDCR4_EDF_RGB888,
>  	}, {
>  		.fourcc = DRM_FORMAT_ARGB8888,
> +		.v4l2 = V4L2_PIX_FMT_ABGR32,
>  		.bpp = 32,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
>  		.edf = PnDDCR4_EDF_ARGB8888,
>  	}, {
>  		.fourcc = DRM_FORMAT_UYVY,
> +		.v4l2 = V4L2_PIX_FMT_UYVY,
>  		.bpp = 16,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_YUYV,
> +		.v4l2 = V4L2_PIX_FMT_YUYV,
>  		.bpp = 16,
>  		.planes = 1,
>  		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_NV12,
> +		.v4l2 = V4L2_PIX_FMT_NV12M,
>  		.bpp = 12,
>  		.planes = 2,
>  		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_NV21,
> +		.v4l2 = V4L2_PIX_FMT_NV21M,
>  		.bpp = 12,
>  		.planes = 2,
>  		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
>  		.edf = PnDDCR4_EDF_NONE,
>  	}, {
>  		.fourcc = DRM_FORMAT_NV16,
> +		.v4l2 = V4L2_PIX_FMT_NV16M,
>  		.bpp = 16,
>  		.planes = 2,
>  		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
> @@ -97,62 +107,77 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
>  	 */
>  	{
>  		.fourcc = DRM_FORMAT_RGB332,
> +		.v4l2 = V4L2_PIX_FMT_RGB332,
>  		.bpp = 8,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_ARGB4444,
> +		.v4l2 = V4L2_PIX_FMT_ARGB444,
>  		.bpp = 16,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_XRGB4444,
> +		.v4l2 = V4L2_PIX_FMT_XRGB444,
>  		.bpp = 16,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_BGR888,
> +		.v4l2 = V4L2_PIX_FMT_RGB24,
>  		.bpp = 24,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_RGB888,
> +		.v4l2 = V4L2_PIX_FMT_BGR24,
>  		.bpp = 24,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_BGRA8888,
> +		.v4l2 = V4L2_PIX_FMT_ARGB32,
>  		.bpp = 32,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_BGRX8888,
> +		.v4l2 = V4L2_PIX_FMT_XRGB32,
>  		.bpp = 32,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_YVYU,
> +		.v4l2 = V4L2_PIX_FMT_YVYU,
>  		.bpp = 16,
>  		.planes = 1,
>  	}, {
>  		.fourcc = DRM_FORMAT_NV61,
> +		.v4l2 = V4L2_PIX_FMT_NV61M,
>  		.bpp = 16,
>  		.planes = 2,
>  	}, {
>  		.fourcc = DRM_FORMAT_YUV420,
> +		.v4l2 = V4L2_PIX_FMT_YUV420M,
>  		.bpp = 12,
>  		.planes = 3,
>  	}, {
>  		.fourcc = DRM_FORMAT_YVU420,
> +		.v4l2 = V4L2_PIX_FMT_YVU420M,
>  		.bpp = 12,
>  		.planes = 3,
>  	}, {
>  		.fourcc = DRM_FORMAT_YUV422,
> +		.v4l2 = V4L2_PIX_FMT_YUV422M,
>  		.bpp = 16,
>  		.planes = 3,
>  	}, {
>  		.fourcc = DRM_FORMAT_YVU422,
> +		.v4l2 = V4L2_PIX_FMT_YVU422M,
>  		.bpp = 16,
>  		.planes = 3,
>  	}, {
>  		.fourcc = DRM_FORMAT_YUV444,
> +		.v4l2 = V4L2_PIX_FMT_YUV444M,
>  		.bpp = 24,
>  		.planes = 3,
>  	}, {
>  		.fourcc = DRM_FORMAT_YVU444,
> +		.v4l2 = V4L2_PIX_FMT_YVU444M,
>  		.bpp = 24,
>  		.planes = 3,

Phew, that was a chore.. But they all match up to their respective
comparisons from the two original tables.

These 'name' inversions are fun:
 DRM_FORMAT_BGR888,	 V4L2_PIX_FMT_RGB24,
 DRM_FORMAT_RGB888,	 V4L2_PIX_FMT_BGR24,
 DRM_FORMAT_BGRA8888,	 V4L2_PIX_FMT_ARGB32,
 DRM_FORMAT_BGRX8888,	 V4L2_PIX_FMT_XRGB32,
 DRM_FORMAT_ARGB8888,	 V4L2_PIX_FMT_ABGR32,
 DRM_FORMAT_XRGB8888,	 V4L2_PIX_FMT_XBGR32,

But they all check out.

>  	},
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
> index e171527abdaa..0346504d8c59 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
> @@ -19,6 +19,7 @@ struct rcar_du_device;
>  
>  struct rcar_du_format_info {
>  	u32 fourcc;
> +	u32 v4l2;
>  	unsigned int bpp;
>  	unsigned int planes;
>  	unsigned int pnmr;
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> index 28bfeb8c24fb..29a08f7b0761 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> @@ -109,8 +109,7 @@ void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
>  	vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
>  }
>  
> -/* Keep the two tables in sync. */
> -static const u32 formats_kms[] = {
> +static const u32 rcar_du_vsp_formats[] = {
>  	DRM_FORMAT_RGB332,
>  	DRM_FORMAT_ARGB4444,
>  	DRM_FORMAT_XRGB4444,
> @@ -138,40 +137,13 @@ static const u32 formats_kms[] = {
>  	DRM_FORMAT_YVU444,
>  };
>  
> -static const u32 formats_v4l2[] = {
> -	V4L2_PIX_FMT_RGB332,
> -	V4L2_PIX_FMT_ARGB444,
> -	V4L2_PIX_FMT_XRGB444,
> -	V4L2_PIX_FMT_ARGB555,
> -	V4L2_PIX_FMT_XRGB555,
> -	V4L2_PIX_FMT_RGB565,
> -	V4L2_PIX_FMT_RGB24,
> -	V4L2_PIX_FMT_BGR24,
> -	V4L2_PIX_FMT_ARGB32,
> -	V4L2_PIX_FMT_XRGB32,
> -	V4L2_PIX_FMT_ABGR32,
> -	V4L2_PIX_FMT_XBGR32,
> -	V4L2_PIX_FMT_UYVY,
> -	V4L2_PIX_FMT_YUYV,
> -	V4L2_PIX_FMT_YVYU,
> -	V4L2_PIX_FMT_NV12M,
> -	V4L2_PIX_FMT_NV21M,
> -	V4L2_PIX_FMT_NV16M,
> -	V4L2_PIX_FMT_NV61M,
> -	V4L2_PIX_FMT_YUV420M,
> -	V4L2_PIX_FMT_YVU420M,
> -	V4L2_PIX_FMT_YUV422M,
> -	V4L2_PIX_FMT_YVU422M,
> -	V4L2_PIX_FMT_YUV444M,
> -	V4L2_PIX_FMT_YVU444M,
> -};
> -
>  static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
>  {
>  	struct rcar_du_vsp_plane_state *state =
>  		to_rcar_vsp_plane_state(plane->plane.state);
>  	struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
>  	struct drm_framebuffer *fb = plane->plane.state->fb;
> +	const struct rcar_du_format_info *format;
>  	struct vsp1_du_atomic_config cfg = {
>  		.pixelformat = 0,
>  		.pitch = fb->pitches[0],
> @@ -194,12 +166,8 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
>  		cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
>  			   + fb->offsets[i];
>  
> -	for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
> -		if (formats_kms[i] == state->format->fourcc) {
> -			cfg.pixelformat = formats_v4l2[i];
> -			break;
> -		}
> -	}
> +	format = rcar_du_format_info(state->format->fourcc);
> +	cfg.pixelformat = format->v4l2;
>  
>  	vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
>  			      plane->index, &cfg);
> @@ -394,8 +362,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
>  
>  		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
>  					       &rcar_du_vsp_plane_funcs,
> -					       formats_kms,
> -					       ARRAY_SIZE(formats_kms),
> +					       rcar_du_vsp_formats,
> +					       ARRAY_SIZE(rcar_du_vsp_formats),
>  					       NULL, type, NULL);
>  		if (ret < 0)
>  			return ret;
> 

-- 
Regards
--
Kieran
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation
  2019-03-11 22:57   ` Kieran Bingham
@ 2019-03-12 15:24     ` Laurent Pinchart
  2019-03-12 20:42       ` Kieran Bingham
  0 siblings, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-12 15:24 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: Liviu Dudau, Laurent Pinchart, James Qian Wang, dri-devel

Hi Kieran,

On Mon, Mar 11, 2019 at 10:57:15PM +0000, Kieran Bingham wrote:
> On 21/02/2019 10:32, Laurent Pinchart wrote:
> > The rcar_du_crtc structure index field contains the CRTC hardware index,
> > not the hardware and software index. Update the documentation
> > accordingly.
> 
> Should this have a fixes tag? It's only trivial - but if so:
> 
> Fixes: 5361cc7f8e91 ("drm: rcar-du: Split CRTC handling to support
> hardware indexing")

I'll add it for correctness, I just hope it won't be automatically
backported to stable.

> Either way,
> 
> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> >  drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
> > index bcb35b0b7612..c478953be092 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
> > @@ -27,7 +27,7 @@ struct rcar_du_vsp;
> >   * @clock: the CRTC functional clock
> >   * @extclock: external pixel dot clock (optional)
> >   * @mmio_offset: offset of the CRTC registers in the DU MMIO block
> > - * @index: CRTC software and hardware index
> > + * @index: CRTC hardware index
> >   * @initialized: whether the CRTC has been initialized and clocks enabled
> >   * @dsysr: cached value of the DSYSR register
> >   * @vblank_enable: whether vblank events are enabled on this CRTC

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation
  2019-03-12 15:24     ` Laurent Pinchart
@ 2019-03-12 20:42       ` Kieran Bingham
  0 siblings, 0 replies; 65+ messages in thread
From: Kieran Bingham @ 2019-03-12 20:42 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Liviu Dudau, Laurent Pinchart, James Qian Wang, dri-devel

Hi Laurent,

On 12/03/2019 15:24, Laurent Pinchart wrote:
> Hi Kieran,
> 
> On Mon, Mar 11, 2019 at 10:57:15PM +0000, Kieran Bingham wrote:
>> On 21/02/2019 10:32, Laurent Pinchart wrote:
>>> The rcar_du_crtc structure index field contains the CRTC hardware index,
>>> not the hardware and software index. Update the documentation
>>> accordingly.
>>
>> Should this have a fixes tag? It's only trivial - but if so:
>>
>> Fixes: 5361cc7f8e91 ("drm: rcar-du: Split CRTC handling to support
>> hardware indexing")
> 
> I'll add it for correctness, I just hope it won't be automatically
> backported to stable.

I would expect that it should only be backported if the patch it fixes
(5361cc7f8e91) has also been backported?


> 
>> Either way,
>>
>> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>> ---
>>>  drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
>>> index bcb35b0b7612..c478953be092 100644
>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
>>> @@ -27,7 +27,7 @@ struct rcar_du_vsp;
>>>   * @clock: the CRTC functional clock
>>>   * @extclock: external pixel dot clock (optional)
>>>   * @mmio_offset: offset of the CRTC registers in the DU MMIO block
>>> - * @index: CRTC software and hardware index
>>> + * @index: CRTC hardware index
>>>   * @initialized: whether the CRTC has been initialized and clocks enabled
>>>   * @dsysr: cached value of the DSYSR register
>>>   * @vblank_enable: whether vblank events are enabled on this CRTC
> 

-- 
Regards
--
Kieran
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions
  2019-02-21 10:39   ` Laurent Pinchart
@ 2019-03-13  0:00     ` Laurent Pinchart
  2020-12-16  2:54       ` Laurent Pinchart
  2019-03-13  9:05     ` Kieran Bingham
  1 sibling, 1 reply; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-13  0:00 UTC (permalink / raw)
  To: Rob Clark; +Cc: James Qian Wang, Liviu Dudau, Kieran Bingham, dri-devel

On Thu, Feb 21, 2019 at 12:39:24PM +0200, Laurent Pinchart wrote:
> Forgot to CC Rob, sorry about that.

Rob, could you take this in your tree ?

> On Thu, Feb 21, 2019 at 12:32:08PM +0200, Laurent Pinchart wrote:
> > The msm_atomic_state_clear() and msm_atomic_state_free() functions are
> > declared but never defined. Remove their prototypes.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> >  drivers/gpu/drm/msm/msm_drv.h | 2 --
> >  1 file changed, 2 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> > index 4e0c6c2f9a86..8f0287e75efb 100644
> > --- a/drivers/gpu/drm/msm/msm_drv.h
> > +++ b/drivers/gpu/drm/msm/msm_drv.h
> > @@ -240,8 +240,6 @@ int msm_atomic_prepare_fb(struct drm_plane *plane,
> >  			  struct drm_plane_state *new_state);
> >  void msm_atomic_commit_tail(struct drm_atomic_state *state);
> >  struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
> > -void msm_atomic_state_clear(struct drm_atomic_state *state);
> > -void msm_atomic_state_free(struct drm_atomic_state *state);
> >  
> >  int msm_gem_init_vma(struct msm_gem_address_space *aspace,
> >  		struct msm_gem_vma *vma, int npages);

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-08 15:02                       ` Liviu Dudau
@ 2019-03-13  0:56                         ` Laurent Pinchart
  0 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-13  0:56 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Laurent Pinchart, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Liviu,

On Fri, Mar 08, 2019 at 03:02:39PM +0000, Liviu Dudau wrote:
> On Fri, Mar 08, 2019 at 02:46:08PM +0200, Laurent Pinchart wrote:
> > On Thu, Mar 07, 2019 at 04:31:40PM +0000, Liviu Dudau wrote:
> >> On Thu, Mar 07, 2019 at 03:48:23PM +0200, Laurent Pinchart wrote:
> >>> On Thu, Mar 07, 2019 at 11:52:18AM +0000, Liviu Dudau wrote:
> >>>> On Wed, Mar 06, 2019 at 08:01:53PM +0200, Laurent Pinchart wrote:
> >>>>> On Wed, Mar 06, 2019 at 02:20:51PM +0000, Liviu Dudau wrote:
> >>>>>> On Wed, Mar 06, 2019 at 01:14:40AM +0200, Laurent Pinchart wrote:
> > 
> > [snip]
> > 
> >>>>>>> We thus have three states for an atomic commit:
> >>>>>>> 
> >>>>>>> - active, where the corresponding registers list address has been
> >>>>>>>   written to the hardware, and processed
> >>>>>>> 
> >>>>>>> - queued, where the corresponding registers list address has been
> >>>>>>>   written to the hardware but not processed yet
> >>>>>>> 
> >>>>>>> - pending, where the corresponding registers list address hasn't been
> >>>>>>>   written to the hardware yet
> >>>>>>> 
> >>>>>>> The status bit mentioned above allows us to tell if a list exists in the
> >>>>>>> queued state.
> >>>>>>> 
> >>>>>>> At frame end time, if the status bit is set, we have potentially lost
> >>>>>>> the race between writing the new registers list and the frame end
> >>>>>>> interrupt, so we wait for one more vblank. Otherwise, if a list was
> >>>>>>> queued, we move it to the active state, and retire the active list. If a
> >>>>>>> list was pending, we write its address to the hardware, and move it to
> >>>>>>> the queued state.
> >>>> 
> >>>> It looks to me like the moving of the pending state into queued state is your
> >>>> opportunity to also in-place modify the writeback registers if the state does
> >>>> not have its own writeback request.
> >>> 
> >>> Moving from pending to queued means the pointer has been given to the
> >>> hardware, but not processed yet. I need to wait until the commit that
> >>> enables writeback is fully processed before modifying it in-place to
> >>> disable writeback, and that's at the frame start following the move from
> >>> the queued state to the active state.
> >> 
> >> I'm not attempting to (re)write your driver, only to explain my thinking process in
> >> a way that is easiest for me:
> > 
> > I wouldn't mind if you attempted to rewrite the driver if it ended up in
> > a better state :-)
> > 
> >> 1. driver prepares a new commit that might have a writeback and sets the
> >> pointer register to the new address. It then marks the commit as queued.
> >> 
> >> (optional) 2. driver receives a new commit that is marked as pending
> >> 
> >> 3. end-of-frame interrupt arrives
> >>      a. HW reads the new address and programs a DMA transfer to update registers.
> >>      b. driver reads the status bit and waits until status == 0.
> > 
> > Step b is not needed as the status bit is set to 0 as soon as the
> > hardware starts the DMA.
> 
> OK, however because I'm not sure when the driver's interrupt handler
> is called, I was trying to be safe and make sure the driver can
> proceed.
> 
> >>      c. driver marks queued commit as active and looks if it has any pending commits
> >>           - if yes, it looks at the pending commit if it has a writeback request
> >> 	        - if no, it updates the pending commit to write the register(s) that disable writeback
> >> 		- moves pending commit to queued
> >> 	  - if no, then it copies active commit and disables writeback. (*)
> > 
> > Pending commits are very rare. Userspace will usually queue the next
> > commit when it receives notification of completion of the previous
> > commit, so the next commit will arrive after vblank, with a single
> > commit per frame interval. At that point there will be an active commit,
> > and no queued commit, and the new commit will become the queued commit.
> > The "no" case will thus be hit in the vast majority of cases, preventing
> > the next userspace commit to be queued for the same frame, delaying it
> > by one frame, and thus halving the frame rate :-(
> 
> I'll guess you're referring to the (*) case of "no", but I'm not sure how the next
> userspace commit will be delayed ..... wait a bit .... a light bulb turned on
> somewhere in the darkness of my mind .... see below.
> 
> >>      d. driver signals vblank and any previous writebacks that might have been programmed
> >>         by the previously active commit
> >> 
> >> 4. ....
> >> 5. profit!
> >> 
> >> 
> >> (*) depending on how the HW behaves, it might be enough to create a stub
> >>     commit that only disables the writeback, with no other update.
> > 
> > A stub is enough, there's no need to reprogram everything.
> 
> Right, in that case, can you not look at the queued commit when the next userspace
> commit comes in and if it is the stub commit overwrite it with your new commit as
> if you're at step c. ?

I can't safely overwrite a queued commit, that's my core issue. Once a
commit is queued to the hardware, it can start being processed at any
time, and that could race with overwriting the commit. The hardware
doesn't give me a way to know if the overwrite took place before the
queued commit was processed, or afterwards.

I have however found a way to fix my issue in what I believe is a safe
way, and I've posted a v6 of the series. In a nutshell, the hardware
allows chaining registers lists. I can thus create a registers lists
with writeback enabled, and a second one that disables the writeback.
The two lists are chained, and the first one is queued to the hardware.
When the hardware finishes processing it, it automatically moves to the
chained list for the next frame, without software intervention. As the
chained list is not queued separately it doesn't interfere with the
mechanism that allows me to test if a list is queued before queuing the
next commit, so I can queue the next commit and it will take over the
chained list if it hasn't been processed yet.

> >> This way writeback commits will always be followed by another commit,
> >> even if it is a dummy one that only disables writeback.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions
  2019-02-21 10:39   ` Laurent Pinchart
  2019-03-13  0:00     ` Laurent Pinchart
@ 2019-03-13  9:05     ` Kieran Bingham
  1 sibling, 0 replies; 65+ messages in thread
From: Kieran Bingham @ 2019-03-13  9:05 UTC (permalink / raw)
  To: Laurent Pinchart, Laurent Pinchart
  Cc: James Qian Wang, Liviu Dudau, dri-devel

Hi Laurent,

On 21/02/2019 10:39, Laurent Pinchart wrote:
> Forgot to CC Rob, sorry about that.
> 
> On Thu, Feb 21, 2019 at 12:32:08PM +0200, Laurent Pinchart wrote:
>> The msm_atomic_state_clear() and msm_atomic_state_free() functions are
>> declared but never defined. Remove their prototypes.

I have confirmed there are no implementations of these prototypes in
drm-next tree.

Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>


>>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>> ---
>>  drivers/gpu/drm/msm/msm_drv.h | 2 --
>>  1 file changed, 2 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
>> index 4e0c6c2f9a86..8f0287e75efb 100644
>> --- a/drivers/gpu/drm/msm/msm_drv.h
>> +++ b/drivers/gpu/drm/msm/msm_drv.h
>> @@ -240,8 +240,6 @@ int msm_atomic_prepare_fb(struct drm_plane *plane,
>>  			  struct drm_plane_state *new_state);
>>  void msm_atomic_commit_tail(struct drm_atomic_state *state);
>>  struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
>> -void msm_atomic_state_clear(struct drm_atomic_state *state);
>> -void msm_atomic_state_free(struct drm_atomic_state *state);
>>  
>>  int msm_gem_init_vma(struct msm_gem_address_space *aspace,
>>  		struct msm_gem_vma *vma, int npages);
> 

-- 
Regards
--
Kieran
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-08 12:24                 ` Laurent Pinchart
@ 2019-03-18 16:59                   ` Brian Starkey
  2019-03-19 10:00                     ` Laurent Pinchart
  0 siblings, 1 reply; 65+ messages in thread
From: Brian Starkey @ 2019-03-18 16:59 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Laurent,

Sorry for the delay, I was travelling last week and didn't find a
chance to digest your diagrams (thanks for all the detail!)

On Fri, Mar 08, 2019 at 02:24:40PM +0200, Laurent Pinchart wrote:
> Hi Brian,
> 
> On Thu, Mar 07, 2019 at 12:28:08PM +0000, Brian Starkey wrote:
> > On Wed, Mar 06, 2019 at 08:22:44PM +0200, Laurent Pinchart wrote:
> > 
> > [snip]
> > 
> > > I can always queue a new one, but I have no way of telling if the newly
> > > queued list raced with the frame end interrupt.
> > > 
> > > There's another register I'm not using that contains a shadow copy of
> > > the registers list DMA address. It mirrors the address programmed by the
> > > driver when there is no DMA of the registers list in progress, and
> > > contains the address the of registers list being DMA'ed when a DMA is in
> > > progress. I don't think I can do much with it, as reading it either
> > > before or after reprogramming (to check if I can reprogram or if the
> > > reprogram has been taken into account) is racy.
> > 
> > When you say it mirrors the address programmed, is that latched when
> > the update is accepted, or it will update the moment you program the
> > address register?
> 
> It is latched when the update is processed, at the next vblank following
> the address programming. The timing diagram below shows vblank, the UPD
> bit I use for synchronization, the registers list address register
> exposed to the CPU, and the internal address used to DMA the registers
> list. The address register is written four times to show how the
> hardware reacts, but in practice there will be a single write per frame,
> right after the beginning of vblank.
> 
>                                       DMA starts
>                                       |     DMA ends
>                                       |     |
>                                       V     V
>                                       ___________
> VBLANK         ______________________|           |________
>                      ________________     ________________
> UPD            _____|                |___|
>                _____ ______ _____________ ________ _______
> ADDR register  __A__X__B___X______C______X___D____X___E___
>                ______________________ ____________________
> ADDR internal  ___________A__________X__________C_________
> 
> I can reprogram the address any number of times I want before the
> vblank, but the update bit mechanism only lets me protect the race
> related to the first write. For any subsequent write I won't be able to
> tell whether it was complete before the hardware started the frame, or
> arrived too late.
> 
> > I assume the latter or you would have thought of this yourself (that
> > seems like a really strange/not-useful behaviour!). But if it is the
> > former you could:
> > 
> >  - In writeback start-of-frame, create a copy of the register list,
> >    disabling writeback.
> >  - Write the address of this copy to the register
> > 
> > If/when an atomic commit comes in before you service the next
> > end-of-frame:
> > 
> >  - Write the address of the new register list
> >  - Check the status register. If the "pending" bit is zero, you know
> >    you had a potential race.
> >     - Check the DMA address register. If it corresponds to the new
> >       scene, the new commit won the race, otherwise it's been delayed
> >       by a frame.
> 
> The issue here is that there's a potential race if the pending (UPD) bit
> is one too. If the commit arrives just before vblank, but the address is
> written just after vblank, after the UPD bit has been reset to 0 by the
> hardware, but before the vblank interrupt is processed, then the commit
> won't be applied yet, and I will have no way to know. Compare the two
> following scenarios:
> 
> 		   [1]       [2] [3]           [4]       [5]
>                     |         |   |             |         |
>                     |         V   V             |         V
>                     V          __________       V          __________
> VBLANK         _______________|          |________________|          |__
>                      _________     _______________________
> UPD            _____|         |___|                       |_____________
>                _____ _____________ _____________ _______________________
> ADDR register  __A__X______B______X______C______X___________D___________
>                ___________________________________________ _____________
> ADDR internal  _______A_______X______B____________________X______D______
> 
> [1] Atomic commit, registers list address write, with writeback enabled
> [2] DMA starts for first atomic commit
> [3] Writeback disable registers list address write
> [4] Next atomic commit, with writeback disabled
> [5] DMA starts for second atomic commit
> 
> 		   [1]       [2] [3]                     [4][5]
>                     |         |   |                       |  |
>                     |         V   V                       V  V
>                     V          __________                  __________
> VBLANK         _______________|          |________________|          |__
>                      _________     _______________________    __________
> UPD            _____|         |___|                       |__|
>                _____ _____________ __________________________ __________
> ADDR register  __A__X______B______X_____________C____________X_____D____
>                ___________________________________________ _____________
> ADDR internal  _______A_______X______B____________________X______C______
> 
> [1] Atomic commit, registers list address write, with writeback enabled
> [2] DMA starts for first atomic commit
> [3] Writeback disable registers list address write
> [4] DMA starts for writeback disable registers list (3)
> [5] Next atomic commit, with writeback disabled, performed right after
>     vblank but befrore the vblank interrupt is serviced
> 
> The UPD bit is 1 after writing the ADDR register the second time in both
> cases. Furthermore, if [4] and [5] are very close in the second case,
> the UPD bit may read 1 just before [5] if the read comes before [4]:
> 
> 	read UPD bit;
> 	/* VBLANK [4] */
> 	write ADDR register;
> 
> I thus can't rely on UPD = 1 before the write meaning that the write was
> performed before vblank, and I can't rely either on the UPD bit after
> write, as it's 1 in both cases.

My mistake, I got the UPD bit the wrong way around. I'm still not
entirely sure why you can't use "ADDR internal" to determine which
side won the race. It shows 'B' in the first case, and 'C' in the
second.

When a new commit comes, unconditionally:
 - Write new address
 - Read status

 if status.UPD == 0 --> You know for sure your new commit was just
                        latched.
 if status.UPD == 1 --> You need to check ADDR internal to see which
			of these three happened:

    1) Your first case happened. We're somewhere in the middle of the
       frame. ADDR internal will show 'B', and you know commit 'D' is
       going on-screen at the next vblank.

    2) Your second case happened. The new commit raced with the
       latching of writeback-disable and "lost". ADDR internal will
       show 'C', and the new commit is delayed by a frame

    3) (Teeny tiny small window) In-between reading status and ADDR
       internal, the new commit was latched. ADDR internal will show
       'D'. You know the new commit "won" so treat it the same as if
       UPD == 0 (which it will be, now).

Anyway, it's all moot now that you've found the chained lists thing -
that sounds ideal. I'll take a look at the new series shortly.

Thanks,
-Brian

> 
> I initially thought I could protect against the race using the following
> procedure.
> 
> - Single atomic commit, no IRQ delay
> 
> 		   [1]       [2]        [3]              [4]
>                     |         |          |                |
>                     |         V          V                V
>                     V          __________                  __________
> VBLANK         _______________|          |________________|          |__
>                      _________    
> UPD            _____|         |_________________________________________
>                _____ ___________________________________________________
> ADDR register  __A__X___________________B_______________________________
>                _______________ _________________________________________
> ADDR internal  _______A_______X______B__________________________________
> 
> [1] Atomic commit, registers list address write, with writeback enabled
> [2] DMA starts for first atomic commit
> [3] Frame start, disable writeback in-place in registers list B
> [4] DMA starts for "patched" registers list, disables writeback
> 
> - Two atomic commits, no IRQ delay
> 
> 		   [1]       [2]  [3]   [4]              [5]
>                     |         |    |     |                |
>                     |         V    V     V                V
>                     V          __________                  __________
> VBLANK         _______________|          |________________|          |__
>                      _________      ______________________
> UPD            _____|         |____|                      |_____________
>                _____ ______________ ____________________________________
> ADDR register  __A__X______B_______X_________________C__________________
>                _______________ ___________________________ _____________
> ADDR internal  _______A_______X_____________B_____________X______C______
> 
> [1] Atomic commit, registers list address write, with writeback enabled
> [2] DMA starts for first atomic commit
> [3] Next atomic commit, registers list address write, with writeback
>     disabled
> [4] Frame start, disable writeback in-place in registers list B
> [5] DMA starts for second atomic commit, disables writeback
> 
> [3] and [4] can happen in any order, as long as they both come before
> [5]. If [3] comes after [5], we're back to the previous case (Single
> atomic commit, no IRQ delay).
> 
> - Single atomic commit, IRQ delay
> 
> 		   [1]       [2]        [3]              [4] [5]
>                     |         |          |                |   |
>                     |         V          V                V   V
>                     V          __________                  __________
> VBLANK         _______________|          |________________|          |__
>                      _________    
> UPD            _____|         |_________________________________________
>                _____ ___________________________________________________
> ADDR register  __A__X___________________B_______________________________
>                _______________ _________________________________________
> ADDR internal  _______A_______X______B__________________________________
> 
> [1] Atomic commit, registers list address write, with writeback enabled
> [2] DMA starts for first atomic commit
> [3] Frame start, IRQ is delayed to [5]
> [4] DMA starts for unmodified registers list, writeback still enable
> [5] disable writeback in-place in registers list B, too late
> 
> Here I need to detect that [5] was delayed after [4], and thus delay the
> completion of the writeback job by one frame. This could be done by
> checking the vblank interrupt status bit, if it is set then vblank
> occurred and raced [5]. However, the same issue can also happen when no
> race occurred if processing of the vblank interrupt for [2] is delayed
> until [3]. Both the vblank interrupt and the frame start interrupt
> status bits will be set, indicate a potential race.
> 
> The time between [2] and [3] is very short compared to the time between
> [3] and [4] and to interrupt latency in general, so we would have lots
> of false positives.
> 
> > >> You don't happen to have a DMA engine trigger or something you could
> > >> use to do the register list modification at a guaranteed time do you?
> > > 
> > > Not that I know of, unfortunately.
> > > 
> > >> Are you always going to be protected by an IOMMU, preventing the
> > >> writeback from trashing physical memory? If that's not the case, then
> > >> the race can have pretty dire consequences.
> > > 
> > > If the IOMMU is enabled in DT, yes. It's a system-level decision.
> > 
> > Well, it's your driver at the end of the day. But for me, a known
> > race-condition which would cause random memory corruption sounds like
> > a really Bad Thing. Halving frame-rate on systems with no IOMMU seems
> > preferable to me.
> 
> It is a really bad thing. I think the decision should be taken by
> Renesas though.
> 
> -- 
> Regards,
> 
> Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
  2019-03-18 16:59                   ` Brian Starkey
@ 2019-03-19 10:00                     ` Laurent Pinchart
  0 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2019-03-19 10:00 UTC (permalink / raw)
  To: Brian Starkey
  Cc: Laurent Pinchart, Liviu Dudau, Kieran Bingham, dri-devel,
	james qian wang (Arm Technology China),
	nd

Hi Brian,

On Mon, Mar 18, 2019 at 04:59:21PM +0000, Brian Starkey wrote:
> Hi Laurent,
> 
> Sorry for the delay, I was travelling last week and didn't find a
> chance to digest your diagrams (thanks for all the detail!)

No worries, and thank you for the time you're spending on this. In the
meantime I've found a solution that solves the race, using an entirely
different mechanism. It's all explained in v6 of the patch series.

> On Fri, Mar 08, 2019 at 02:24:40PM +0200, Laurent Pinchart wrote:
> > On Thu, Mar 07, 2019 at 12:28:08PM +0000, Brian Starkey wrote:
> > > On Wed, Mar 06, 2019 at 08:22:44PM +0200, Laurent Pinchart wrote:
> > > 
> > > [snip]
> > > 
> > > > I can always queue a new one, but I have no way of telling if the newly
> > > > queued list raced with the frame end interrupt.
> > > > 
> > > > There's another register I'm not using that contains a shadow copy of
> > > > the registers list DMA address. It mirrors the address programmed by the
> > > > driver when there is no DMA of the registers list in progress, and
> > > > contains the address the of registers list being DMA'ed when a DMA is in
> > > > progress. I don't think I can do much with it, as reading it either
> > > > before or after reprogramming (to check if I can reprogram or if the
> > > > reprogram has been taken into account) is racy.
> > > 
> > > When you say it mirrors the address programmed, is that latched when
> > > the update is accepted, or it will update the moment you program the
> > > address register?
> > 
> > It is latched when the update is processed, at the next vblank following
> > the address programming. The timing diagram below shows vblank, the UPD
> > bit I use for synchronization, the registers list address register
> > exposed to the CPU, and the internal address used to DMA the registers
> > list. The address register is written four times to show how the
> > hardware reacts, but in practice there will be a single write per frame,
> > right after the beginning of vblank.
> > 
> >                                       DMA starts
> >                                       |     DMA ends
> >                                       |     |
> >                                       V     V
> >                                       ___________
> > VBLANK         ______________________|           |________
> >                      ________________     ________________
> > UPD            _____|                |___|
> >                _____ ______ _____________ ________ _______
> > ADDR register  __A__X__B___X______C______X___D____X___E___
> >                ______________________ ____________________
> > ADDR internal  ___________A__________X__________C_________
> > 
> > I can reprogram the address any number of times I want before the
> > vblank, but the update bit mechanism only lets me protect the race
> > related to the first write. For any subsequent write I won't be able to
> > tell whether it was complete before the hardware started the frame, or
> > arrived too late.
> > 
> > > I assume the latter or you would have thought of this yourself (that
> > > seems like a really strange/not-useful behaviour!). But if it is the
> > > former you could:
> > > 
> > >  - In writeback start-of-frame, create a copy of the register list,
> > >    disabling writeback.
> > >  - Write the address of this copy to the register
> > > 
> > > If/when an atomic commit comes in before you service the next
> > > end-of-frame:
> > > 
> > >  - Write the address of the new register list
> > >  - Check the status register. If the "pending" bit is zero, you know
> > >    you had a potential race.
> > >     - Check the DMA address register. If it corresponds to the new
> > >       scene, the new commit won the race, otherwise it's been delayed
> > >       by a frame.
> > 
> > The issue here is that there's a potential race if the pending (UPD) bit
> > is one too. If the commit arrives just before vblank, but the address is
> > written just after vblank, after the UPD bit has been reset to 0 by the
> > hardware, but before the vblank interrupt is processed, then the commit
> > won't be applied yet, and I will have no way to know. Compare the two
> > following scenarios:
> > 
> > 		   [1]       [2] [3]           [4]       [5]
> >                     |         |   |             |         |
> >                     |         V   V             |         V
> >                     V          __________       V          __________
> > VBLANK         _______________|          |________________|          |__
> >                      _________     _______________________
> > UPD            _____|         |___|                       |_____________
> >                _____ _____________ _____________ _______________________
> > ADDR register  __A__X______B______X______C______X___________D___________
> >                ___________________________________________ _____________
> > ADDR internal  _______A_______X______B____________________X______D______
> > 
> > [1] Atomic commit, registers list address write, with writeback enabled
> > [2] DMA starts for first atomic commit
> > [3] Writeback disable registers list address write
> > [4] Next atomic commit, with writeback disabled
> > [5] DMA starts for second atomic commit
> > 
> > 		   [1]       [2] [3]                     [4][5]
> >                     |         |   |                       |  |
> >                     |         V   V                       V  V
> >                     V          __________                  __________
> > VBLANK         _______________|          |________________|          |__
> >                      _________     _______________________    __________
> > UPD            _____|         |___|                       |__|
> >                _____ _____________ __________________________ __________
> > ADDR register  __A__X______B______X_____________C____________X_____D____
> >                ___________________________________________ _____________
> > ADDR internal  _______A_______X______B____________________X______C______
> > 
> > [1] Atomic commit, registers list address write, with writeback enabled
> > [2] DMA starts for first atomic commit
> > [3] Writeback disable registers list address write
> > [4] DMA starts for writeback disable registers list (3)
> > [5] Next atomic commit, with writeback disabled, performed right after
> >     vblank but befrore the vblank interrupt is serviced
> > 
> > The UPD bit is 1 after writing the ADDR register the second time in both
> > cases. Furthermore, if [4] and [5] are very close in the second case,
> > the UPD bit may read 1 just before [5] if the read comes before [4]:
> > 
> > 	read UPD bit;
> > 	/* VBLANK [4] */
> > 	write ADDR register;
> > 
> > I thus can't rely on UPD = 1 before the write meaning that the write was
> > performed before vblank, and I can't rely either on the UPD bit after
> > write, as it's 1 in both cases.
> 
> My mistake, I got the UPD bit the wrong way around. I'm still not
> entirely sure why you can't use "ADDR internal" to determine which
> side won the race. It shows 'B' in the first case, and 'C' in the
> second.

Because ADDR internal isn't available to the CPU :-(

> When a new commit comes, unconditionally:
>  - Write new address
>  - Read status
> 
>  if status.UPD == 0 --> You know for sure your new commit was just
>                         latched.
>  if status.UPD == 1 --> You need to check ADDR internal to see which
> 			of these three happened:
> 
>     1) Your first case happened. We're somewhere in the middle of the
>        frame. ADDR internal will show 'B', and you know commit 'D' is
>        going on-screen at the next vblank.
> 
>     2) Your second case happened. The new commit raced with the
>        latching of writeback-disable and "lost". ADDR internal will
>        show 'C', and the new commit is delayed by a frame
> 
>     3) (Teeny tiny small window) In-between reading status and ADDR
>        internal, the new commit was latched. ADDR internal will show
>        'D'. You know the new commit "won" so treat it the same as if
>        UPD == 0 (which it will be, now).
> 
> Anyway, it's all moot now that you've found the chained lists thing -
> that sounds ideal. I'll take a look at the new series shortly.

It's a neat hardware feature, yes. We were already using it for a
different purpose, I should have thought about it for writeback too from
the very beginning.

Please note I've sent a pull request for v7 as it has been fully
reviewed. Nonetheless, if you find issues, I can fix them on top.

> > I initially thought I could protect against the race using the following
> > procedure.
> > 
> > - Single atomic commit, no IRQ delay
> > 
> > 		   [1]       [2]        [3]              [4]
> >                     |         |          |                |
> >                     |         V          V                V
> >                     V          __________                  __________
> > VBLANK         _______________|          |________________|          |__
> >                      _________    
> > UPD            _____|         |_________________________________________
> >                _____ ___________________________________________________
> > ADDR register  __A__X___________________B_______________________________
> >                _______________ _________________________________________
> > ADDR internal  _______A_______X______B__________________________________
> > 
> > [1] Atomic commit, registers list address write, with writeback enabled
> > [2] DMA starts for first atomic commit
> > [3] Frame start, disable writeback in-place in registers list B
> > [4] DMA starts for "patched" registers list, disables writeback
> > 
> > - Two atomic commits, no IRQ delay
> > 
> > 		   [1]       [2]  [3]   [4]              [5]
> >                     |         |    |     |                |
> >                     |         V    V     V                V
> >                     V          __________                  __________
> > VBLANK         _______________|          |________________|          |__
> >                      _________      ______________________
> > UPD            _____|         |____|                      |_____________
> >                _____ ______________ ____________________________________
> > ADDR register  __A__X______B_______X_________________C__________________
> >                _______________ ___________________________ _____________
> > ADDR internal  _______A_______X_____________B_____________X______C______
> > 
> > [1] Atomic commit, registers list address write, with writeback enabled
> > [2] DMA starts for first atomic commit
> > [3] Next atomic commit, registers list address write, with writeback
> >     disabled
> > [4] Frame start, disable writeback in-place in registers list B
> > [5] DMA starts for second atomic commit, disables writeback
> > 
> > [3] and [4] can happen in any order, as long as they both come before
> > [5]. If [3] comes after [5], we're back to the previous case (Single
> > atomic commit, no IRQ delay).
> > 
> > - Single atomic commit, IRQ delay
> > 
> > 		   [1]       [2]        [3]              [4] [5]
> >                     |         |          |                |   |
> >                     |         V          V                V   V
> >                     V          __________                  __________
> > VBLANK         _______________|          |________________|          |__
> >                      _________    
> > UPD            _____|         |_________________________________________
> >                _____ ___________________________________________________
> > ADDR register  __A__X___________________B_______________________________
> >                _______________ _________________________________________
> > ADDR internal  _______A_______X______B__________________________________
> > 
> > [1] Atomic commit, registers list address write, with writeback enabled
> > [2] DMA starts for first atomic commit
> > [3] Frame start, IRQ is delayed to [5]
> > [4] DMA starts for unmodified registers list, writeback still enable
> > [5] disable writeback in-place in registers list B, too late
> > 
> > Here I need to detect that [5] was delayed after [4], and thus delay the
> > completion of the writeback job by one frame. This could be done by
> > checking the vblank interrupt status bit, if it is set then vblank
> > occurred and raced [5]. However, the same issue can also happen when no
> > race occurred if processing of the vblank interrupt for [2] is delayed
> > until [3]. Both the vblank interrupt and the frame start interrupt
> > status bits will be set, indicate a potential race.
> > 
> > The time between [2] and [3] is very short compared to the time between
> > [3] and [4] and to interrupt latency in general, so we would have lots
> > of false positives.
> > 
> > > >> You don't happen to have a DMA engine trigger or something you could
> > > >> use to do the register list modification at a guaranteed time do you?
> > > > 
> > > > Not that I know of, unfortunately.
> > > > 
> > > >> Are you always going to be protected by an IOMMU, preventing the
> > > >> writeback from trashing physical memory? If that's not the case, then
> > > >> the race can have pretty dire consequences.
> > > > 
> > > > If the IOMMU is enabled in DT, yes. It's a system-level decision.
> > > 
> > > Well, it's your driver at the end of the day. But for me, a known
> > > race-condition which would cause random memory corruption sounds like
> > > a really Bad Thing. Halving frame-rate on systems with no IOMMU seems
> > > preferable to me.
> > 
> > It is a really bad thing. I think the decision should be taken by
> > Renesas though.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions
  2019-03-13  0:00     ` Laurent Pinchart
@ 2020-12-16  2:54       ` Laurent Pinchart
  0 siblings, 0 replies; 65+ messages in thread
From: Laurent Pinchart @ 2020-12-16  2:54 UTC (permalink / raw)
  To: Rob Clark; +Cc: Liviu Dudau, James Qian Wang, Kieran Bingham, dri-devel

On Wed, Mar 13, 2019 at 02:00:28AM +0200, Laurent Pinchart wrote:
> On Thu, Feb 21, 2019 at 12:39:24PM +0200, Laurent Pinchart wrote:
> > Forgot to CC Rob, sorry about that.
> 
> Rob, could you take this in your tree ?

Gentle ping.

> > On Thu, Feb 21, 2019 at 12:32:08PM +0200, Laurent Pinchart wrote:
> > > The msm_atomic_state_clear() and msm_atomic_state_free() functions are
> > > declared but never defined. Remove their prototypes.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > > ---
> > >  drivers/gpu/drm/msm/msm_drv.h | 2 --
> > >  1 file changed, 2 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> > > index 4e0c6c2f9a86..8f0287e75efb 100644
> > > --- a/drivers/gpu/drm/msm/msm_drv.h
> > > +++ b/drivers/gpu/drm/msm/msm_drv.h
> > > @@ -240,8 +240,6 @@ int msm_atomic_prepare_fb(struct drm_plane *plane,
> > >  			  struct drm_plane_state *new_state);
> > >  void msm_atomic_commit_tail(struct drm_atomic_state *state);
> > >  struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
> > > -void msm_atomic_state_clear(struct drm_atomic_state *state);
> > > -void msm_atomic_state_free(struct drm_atomic_state *state);
> > >  
> > >  int msm_gem_init_vma(struct msm_gem_address_space *aspace,
> > >  		struct msm_gem_vma *vma, int npages);

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2020-12-16  2:54 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-21 10:31 [PATCH v5 00/19] R-Car DU display writeback support Laurent Pinchart
2019-02-21 10:31 ` [PATCH v5 01/19] Revert "[media] v4l: vsp1: Supply frames to the DU continuously" Laurent Pinchart
2019-02-21 13:16   ` Kieran Bingham
2019-02-21 10:31 ` [PATCH v5 02/19] media: vsp1: wpf: Fix partition configuration for display pipelines Laurent Pinchart
2019-02-21 10:31 ` [PATCH v5 03/19] media: vsp1: Replace leftover occurrence of fragment with body Laurent Pinchart
2019-02-21 10:31 ` [PATCH v5 04/19] media: vsp1: Fix addresses of display-related registers for VSP-DL Laurent Pinchart
2019-02-21 10:31 ` [PATCH v5 05/19] media: vsp1: Refactor vsp1_video_complete_buffer() for later reuse Laurent Pinchart
2019-02-21 10:31 ` [PATCH v5 06/19] media: vsp1: Replace the display list internal flag with a flags field Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list Laurent Pinchart
2019-02-21 13:16   ` Kieran Bingham
2019-02-22 14:30   ` Brian Starkey
2019-02-22 14:46     ` Laurent Pinchart
2019-02-22 15:06       ` Brian Starkey
2019-03-05 23:14         ` Laurent Pinchart
2019-03-06 11:05           ` Brian Starkey
2019-03-06 18:22             ` Laurent Pinchart
2019-03-07 12:28               ` Brian Starkey
2019-03-08 12:24                 ` Laurent Pinchart
2019-03-18 16:59                   ` Brian Starkey
2019-03-19 10:00                     ` Laurent Pinchart
2019-03-06 14:20           ` Liviu Dudau
2019-03-06 18:01             ` Laurent Pinchart
2019-03-07 11:52               ` Liviu Dudau
2019-03-07 13:48                 ` Laurent Pinchart
2019-03-07 16:31                   ` Liviu Dudau
2019-03-08 12:46                     ` Laurent Pinchart
2019-03-08 15:02                       ` Liviu Dudau
2019-03-13  0:56                         ` Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 08/19] media: vsp1: wpf: Add writeback support Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 09/19] media: vsp1: drm: Split RPF format setting to separate function Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 10/19] media: vsp1: drm: Extend frame completion API to the DU driver Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 11/19] media: vsp1: drm: Implement writeback support Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 12/19] drm: writeback: Cleanup job ownership handling when queuing job Laurent Pinchart
2019-02-21 10:42   ` Laurent Pinchart
2019-02-21 16:02     ` Brian Starkey
2019-02-21 21:56       ` Laurent Pinchart
2019-02-22 13:33         ` Brian Starkey
2019-02-21 16:40   ` Eric Anholt
2019-02-26 18:07   ` Liviu Dudau
2019-02-21 10:32 ` [PATCH v5 13/19] drm: writeback: Fix leak of writeback job Laurent Pinchart
2019-02-21 17:48   ` Brian Starkey
2019-02-26 18:10   ` Liviu Dudau
2019-02-21 10:32 ` [PATCH v5 14/19] drm: writeback: Add job prepare and cleanup operations Laurent Pinchart
2019-02-21 18:12   ` Brian Starkey
2019-02-21 22:12     ` Laurent Pinchart
2019-02-22 13:50       ` Brian Starkey
2019-02-22 14:49         ` Laurent Pinchart
2019-02-22 15:11           ` Brian Starkey
2019-02-26 18:39   ` Liviu Dudau
2019-02-27 12:38     ` Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 15/19] drm/msm: Remove prototypes for non-existing functions Laurent Pinchart
2019-02-21 10:39   ` Laurent Pinchart
2019-03-13  0:00     ` Laurent Pinchart
2020-12-16  2:54       ` Laurent Pinchart
2019-03-13  9:05     ` Kieran Bingham
2019-02-21 10:32 ` [PATCH v5 16/19] drm: rcar-du: Fix rcar_du_crtc structure documentation Laurent Pinchart
2019-03-11 22:57   ` Kieran Bingham
2019-03-12 15:24     ` Laurent Pinchart
2019-03-12 20:42       ` Kieran Bingham
2019-02-21 10:32 ` [PATCH v5 17/19] drm: rcar-du: Store V4L2 fourcc in rcar_du_format_info structure Laurent Pinchart
2019-03-11 23:20   ` Kieran Bingham
2019-02-21 10:32 ` [PATCH v5 18/19] drm: rcar-du: vsp: Extract framebuffer (un)mapping to separate functions Laurent Pinchart
2019-02-21 10:32 ` [PATCH v5 19/19] drm: rcar-du: Add writeback support for R-Car Gen3 Laurent Pinchart
2019-02-22 14:04 ` [PATCH v5 00/19] R-Car DU display writeback support Brian Starkey
2019-02-22 14:47   ` Laurent Pinchart

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.