linux-rockchip.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Samuel Holland <samuel@sholland.org>
To: "Heiko Stübner" <heiko@sntech.de>,
	"Sandy Huang" <hjc@rock-chips.com>,
	dri-devel@lists.freedesktop.org
Cc: linux-rockchip@lists.infradead.org,
	"Alistair Francis" <alistair@alistair23.me>,
	"Ondřej Jirman" <x@xff.cz>,
	"Andreas Kemnade" <andreas@kemnade.info>,
	"Daniel Vetter" <daniel@ffwll.ch>,
	"David Airlie" <airlied@linux.ie>,
	"Geert Uytterhoeven" <geert@linux-m68k.org>,
	"Samuel Holland" <samuel@sholland.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Liang Chen" <cl@rock-chips.com>,
	"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
	"Maxime Ripard" <mripard@kernel.org>,
	"Michael Riesch" <michael.riesch@wolfvision.net>,
	"Nicolas Frattaroli" <frattaroli.nicolas@gmail.com>,
	"Peter Geis" <pgwipeout@gmail.com>,
	"Rob Herring" <robh+dt@kernel.org>,
	"Sam Ravnborg" <sam@ravnborg.org>,
	"Thierry Reding" <thierry.reding@gmail.com>,
	"Thomas Zimmermann" <tzimmermann@suse.de>,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: [RFC PATCH 07/16] drm/rockchip: ebc: Add CRTC buffer management
Date: Wed, 13 Apr 2022 17:19:07 -0500	[thread overview]
Message-ID: <20220413221916.50995-8-samuel@sholland.org> (raw)
In-Reply-To: <20220413221916.50995-1-samuel@sholland.org>

This commit adds the "context" structure which holds all buffers needed
to refresh the display. It is allocated separately from the CRTC state
because it is reused as long as no mode change occurs.

There are three buffers holding Y4 (grayscale 4 bits/pixel) pixel data:
  - "prev" - contents of the display at the beginning of a refresh.
  - "next" - contents of the display at the end of that refresh. When a
    refresh finishes, the "next" buffer is copied into "prev".
  - "final" - contents of the display at the end of the final refresh.
    This buffer is necessary because a refresh waveform cannot be
    modified or interrupted once it is started. If a pixel's value is
    changed while it is already being refreshed, the "next" buffer
    cannot be updated until the first waveform completes. At that time,
    the "final" buffer is copied into "next", and another refresh is
    started. The name "final" refers to the write-combining behavior of
    this buffer; any number of plane updates can change this buffer
    while waiting for the current refresh to complete.

Then there are two buffers holding "phase" data. These are only used
during partial refreshes. The phase represents the time component (the X
coordinate) of the waveform. Since the EBC supports a maximum of 256
phases in a waveform, the phase number requires one byte per pixel. The
driver swaps between two buffers to minimize the delay between phases,
as these buffers must be updated for every phase in the waveform.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

 drivers/gpu/drm/rockchip/rockchip_ebc.c | 166 +++++++++++++++++++++++-
 1 file changed, 163 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c
index ebe60d5e011a..095d66e67c2f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_ebc.c
+++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c
@@ -17,6 +17,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_epd_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -141,6 +142,10 @@ struct rockchip_ebc {
 	bool				reset_complete;
 };
 
+static int default_waveform = DRM_EPD_WF_GC16;
+module_param(default_waveform, int, 0644);
+MODULE_PARM_DESC(default_waveform, "waveform to use for display updates");
+
 static bool skip_reset;
 module_param(skip_reset, bool, 0444);
 MODULE_PARM_DESC(skip_reset, "skip the initial display reset");
@@ -165,12 +170,86 @@ static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = {
 	.atomic_commit		= drm_atomic_helper_commit,
 };
 
+/**
+ * struct rockchip_ebc_ctx - context for performing display refreshes
+ *
+ * @kref: Reference count, maintained as part of the CRTC's atomic state
+ * @prev: Display contents (Y4) before this refresh
+ * @next: Display contents (Y4) after this refresh
+ * @final: Display contents (Y4) after all pending refreshes
+ * @phase: Buffers for selecting a phase from the EBC's LUT, 1 byte/pixel
+ * @gray4_pitch: Horizontal line length of a Y4 pixel buffer in bytes
+ * @gray4_size: Size of a Y4 pixel buffer in bytes
+ * @phase_pitch: Horizontal line length of a phase buffer in bytes
+ * @phase_size: Size of a phase buffer in bytes
+ */
+struct rockchip_ebc_ctx {
+	struct kref			kref;
+	u8				*prev;
+	u8				*next;
+	u8				*final;
+	u8				*phase[2];
+	u32				gray4_pitch;
+	u32				gray4_size;
+	u32				phase_pitch;
+	u32				phase_size;
+};
+
+static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx)
+{
+	kfree(ctx->prev);
+	kfree(ctx->next);
+	kfree(ctx->final);
+	kfree(ctx->phase[0]);
+	kfree(ctx->phase[1]);
+	kfree(ctx);
+}
+
+static struct rockchip_ebc_ctx *rockchip_ebc_ctx_alloc(u32 width, u32 height)
+{
+	u32 gray4_size = width * height / 2;
+	u32 phase_size = width * height;
+	struct rockchip_ebc_ctx *ctx;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+
+	ctx->prev = kmalloc(gray4_size, GFP_KERNEL);
+	ctx->next = kmalloc(gray4_size, GFP_KERNEL);
+	ctx->final = kmalloc(gray4_size, GFP_KERNEL);
+	ctx->phase[0] = kmalloc(phase_size, GFP_KERNEL);
+	ctx->phase[1] = kmalloc(phase_size, GFP_KERNEL);
+	if (!ctx->prev || !ctx->next || !ctx->final ||
+	    !ctx->phase[0] || !ctx->phase[1]) {
+		rockchip_ebc_ctx_free(ctx);
+		return NULL;
+	}
+
+	kref_init(&ctx->kref);
+	ctx->gray4_pitch = width / 2;
+	ctx->gray4_size  = gray4_size;
+	ctx->phase_pitch = width;
+	ctx->phase_size  = phase_size;
+
+	return ctx;
+}
+
+static void rockchip_ebc_ctx_release(struct kref *kref)
+{
+	struct rockchip_ebc_ctx *ctx =
+		container_of(kref, struct rockchip_ebc_ctx, kref);
+
+	return rockchip_ebc_ctx_free(ctx);
+}
+
 /*
  * CRTC
  */
 
 struct ebc_crtc_state {
 	struct drm_crtc_state		base;
+	struct rockchip_ebc_ctx		*ctx;
 };
 
 static inline struct ebc_crtc_state *
@@ -179,11 +258,70 @@ to_ebc_crtc_state(struct drm_crtc_state *crtc_state)
 	return container_of(crtc_state, struct ebc_crtc_state, base);
 }
 
+static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc,
+					const struct rockchip_ebc_ctx *ctx)
+{
+	struct drm_device *drm = &ebc->drm;
+	u32 gray4_size = ctx->gray4_size;
+
+	drm_dbg(drm, "global refresh\n");
+
+	memcpy(ctx->prev, ctx->next, gray4_size);
+}
+
+static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc,
+					 struct rockchip_ebc_ctx *ctx)
+{
+	struct drm_device *drm = &ebc->drm;
+
+	drm_dbg(drm, "partial refresh\n");
+}
+
+static void rockchip_ebc_refresh(struct rockchip_ebc *ebc,
+				 struct rockchip_ebc_ctx *ctx,
+				 bool global_refresh,
+				 enum drm_epd_waveform waveform)
+{
+	if (global_refresh)
+		rockchip_ebc_global_refresh(ebc, ctx);
+	else
+		rockchip_ebc_partial_refresh(ebc, ctx);
+}
+
 static int rockchip_ebc_refresh_thread(void *data)
 {
 	struct rockchip_ebc *ebc = data;
+	struct rockchip_ebc_ctx *ctx;
 
 	while (!kthread_should_stop()) {
+		/* The context will change each time the thread is unparked. */
+		ctx = to_ebc_crtc_state(READ_ONCE(ebc->crtc.state))->ctx;
+
+		/*
+		 * Initialize the buffers before use. This is deferred to the
+		 * kthread to avoid slowing down atomic_check.
+		 *
+		 * ctx->prev and ctx->next are set to 0xff, all white, because:
+		 *  1) the display is set to white by the reset waveform, and
+		 *  2) the driver maintains the invariant that the display is
+		 *     all white whenever the CRTC is disabled.
+		 *
+		 * ctx->final is initialized by the first plane update.
+		 *
+		 * ctx->phase is set to 0xff, the number of the last possible
+		 * phase, because the LUT for that phase is known to be all
+		 * zeroes. (The last real phase in a waveform is zero in order
+		 * to discharge the display, and unused phases in the LUT are
+		 * zeroed out.) This prevents undesired driving of the display
+		 * in 3-window mode between when the framebuffer is blitted
+		 * (and thus prev != next) and when the refresh thread starts
+		 * counting phases for that region.
+		 */
+		memset(ctx->prev, 0xff, ctx->gray4_size);
+		memset(ctx->next, 0xff, ctx->gray4_size);
+		memset(ctx->phase[0], 0xff, ctx->phase_size);
+		memset(ctx->phase[1], 0xff, ctx->phase_size);
+
 		/*
 		 * LUTs use both the old and the new pixel values as inputs.
 		 * However, the initial contents of the display are unknown.
@@ -192,11 +330,11 @@ static int rockchip_ebc_refresh_thread(void *data)
 		 */
 		if (!ebc->reset_complete) {
 			ebc->reset_complete = true;
-			drm_dbg(&ebc->drm, "display reset\n");
+			rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_RESET);
 		}
 
 		while (!kthread_should_park()) {
-			drm_dbg(&ebc->drm, "display update\n");
+			rockchip_ebc_refresh(ebc, ctx, false, default_waveform);
 
 			set_current_state(TASK_IDLE);
 			schedule();
@@ -207,7 +345,8 @@ static int rockchip_ebc_refresh_thread(void *data)
 		 * Clear the display before disabling the CRTC. Use the
 		 * highest-quality waveform to minimize visible artifacts.
 		 */
-		drm_dbg(&ebc->drm, "display clear\n");
+		memset(ctx->next, 0xff, ctx->gray4_size);
+		rockchip_ebc_refresh(ebc, ctx, true, DRM_EPD_WF_GC16);
 
 		kthread_parkme();
 	}
@@ -312,7 +451,9 @@ static int rockchip_ebc_crtc_atomic_check(struct drm_crtc *crtc,
 					  struct drm_atomic_state *state)
 {
 	struct rockchip_ebc *ebc = crtc_to_ebc(crtc);
+	struct ebc_crtc_state *ebc_crtc_state;
 	struct drm_crtc_state *crtc_state;
+	struct rockchip_ebc_ctx *ctx;
 
 	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
 	if (!crtc_state->mode_changed)
@@ -320,14 +461,26 @@ static int rockchip_ebc_crtc_atomic_check(struct drm_crtc *crtc,
 
 	if (crtc_state->enable) {
 		struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+
 		long rate = mode->clock * 1000;
 
 		rate = clk_round_rate(ebc->dclk, rate);
 		if (rate < 0)
 			return rate;
 		mode->clock = rate / 1000;
+
+		ctx = rockchip_ebc_ctx_alloc(mode->hdisplay, mode->vdisplay);
+		if (!ctx)
+			return -ENOMEM;
+	} else {
+		ctx = NULL;
 	}
 
+	ebc_crtc_state = to_ebc_crtc_state(crtc_state);
+	if (ebc_crtc_state->ctx)
+		kref_put(&ebc_crtc_state->ctx->kref, rockchip_ebc_ctx_release);
+	ebc_crtc_state->ctx = ctx;
+
 	return 0;
 }
 
@@ -397,6 +550,10 @@ rockchip_ebc_crtc_duplicate_state(struct drm_crtc *crtc)
 
 	__drm_atomic_helper_crtc_duplicate_state(crtc, &ebc_crtc_state->base);
 
+	ebc_crtc_state->ctx = to_ebc_crtc_state(crtc->state)->ctx;
+	if (ebc_crtc_state->ctx)
+		kref_get(&ebc_crtc_state->ctx->kref);
+
 	return &ebc_crtc_state->base;
 }
 
@@ -405,6 +562,9 @@ static void rockchip_ebc_crtc_destroy_state(struct drm_crtc *crtc,
 {
 	struct ebc_crtc_state *ebc_crtc_state = to_ebc_crtc_state(crtc_state);
 
+	if (ebc_crtc_state->ctx)
+		kref_put(&ebc_crtc_state->ctx->kref, rockchip_ebc_ctx_release);
+
 	__drm_atomic_helper_crtc_destroy_state(&ebc_crtc_state->base);
 
 	kfree(ebc_crtc_state);
-- 
2.35.1


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

  parent reply	other threads:[~2022-04-13 22:23 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-13 22:19 [RFC PATCH 00/16] drm/rockchip: Rockchip EBC ("E-Book Controller") display driver Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 01/16] drm: Add a helper library for EPD drivers Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 02/16] dt-bindings: display: rockchip: Add EBC binding Samuel Holland
2022-04-14  8:15   ` Andreas Kemnade
2022-04-15  3:00     ` Samuel Holland
2022-04-15 11:45       ` Andreas Kemnade
2022-04-13 22:19 ` [RFC PATCH 03/16] drm/rockchip: Add EBC platform driver skeleton Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 04/16] drm/rockchip: ebc: Add DRM " Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 05/16] drm/rockchip: ebc: Add CRTC mode setting Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 06/16] drm/rockchip: ebc: Add CRTC refresh thread Samuel Holland
2022-04-13 22:19 ` Samuel Holland [this message]
2022-04-13 22:19 ` [RFC PATCH 08/16] drm/rockchip: ebc: Add LUT loading Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 09/16] drm/rockchip: ebc: Implement global refreshes Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 10/16] drm/rockchip: ebc: Implement partial refreshes Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 11/16] drm/rockchip: ebc: Enable diff mode for " Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 12/16] drm/rockchip: ebc: Add support for direct mode Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 13/16] drm/rockchip: ebc: Add a panel reflection option Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 14/16] drm/panel-simple: Add eInk ED103TC2 Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 15/16] arm64: dts: rockchip: rk356x: Add EBC node Samuel Holland
2022-04-13 22:19 ` [RFC PATCH 16/16] [DO NOT MERGE] arm64: dts: rockchip: pinenote: Enable EBC display pipeline Samuel Holland
2022-04-14  8:50 ` [RFC PATCH 00/16] drm/rockchip: Rockchip EBC ("E-Book Controller") display driver Maxime Ripard
2022-05-25 17:18   ` Daniel Vetter
2022-05-31  8:58     ` Maxime Ripard
2022-06-01 12:35       ` Daniel Vetter
2022-06-08 14:48         ` Maxime Ripard
2022-06-08 15:34           ` Daniel Vetter
2022-04-21  6:43 ` Andreas Kemnade
2022-04-21  7:10   ` Nicolas Frattaroli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220413221916.50995-8-samuel@sholland.org \
    --to=samuel@sholland.org \
    --cc=airlied@linux.ie \
    --cc=alistair@alistair23.me \
    --cc=andreas@kemnade.info \
    --cc=cl@rock-chips.com \
    --cc=daniel@ffwll.ch \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=frattaroli.nicolas@gmail.com \
    --cc=geert@linux-m68k.org \
    --cc=heiko@sntech.de \
    --cc=hjc@rock-chips.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=michael.riesch@wolfvision.net \
    --cc=mripard@kernel.org \
    --cc=pgwipeout@gmail.com \
    --cc=robh+dt@kernel.org \
    --cc=sam@ravnborg.org \
    --cc=thierry.reding@gmail.com \
    --cc=tzimmermann@suse.de \
    --cc=x@xff.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).