From: Alexandru-Cosmin Gheorghe <Alexandru-Cosmin.Gheorghe@arm.com>
To: Daniel Vetter <daniel@ffwll.ch>
Cc: Thomas Hellstrom <thellstrom@vmware.com>,
nd@arm.com, "petri.latvala@intel.com" <petri.latvala@intel.com>,
Daniel Vetter <daniel.vetter@ffwll.ch>,
"intel-gfx@lists.freedesktop.org"
<intel-gfx@lists.freedesktop.org>,
"dri-devel@lists.freedesktop.org"
<dri-devel@lists.freedesktop.org>,
"igt-dev@lists.freedesktop.org" <igt-dev@lists.freedesktop.org>,
linux-graphics-maintainer <linux-graphics-maintainer@vmware.com>,
Deepak Singh Rawat <drawat@vmware.com>
Subject: Re: [PATCH v3 04/18] drm/selftest: Add drm damage helper selftest
Date: Tue, 16 Oct 2018 13:52:08 +0100 [thread overview]
Message-ID: <20181016125208.GA21819@e114479-lin.cambridge.arm.com> (raw)
In-Reply-To: <20181016122117.GR31561@phenom.ffwll.local>
On Tue, Oct 16, 2018 at 02:21:17PM +0200, Daniel Vetter wrote:
> On Mon, Oct 15, 2018 at 04:11:41PM +0000, Deepak Singh Rawat wrote:
> > > On Wed, Oct 10, 2018 at 05:16:43PM -0700, Deepak Rawat wrote:
> > > > Selftest for drm damage helper iterator functions.
> > > >
> > > > Cc: ville.syrjala@linux.intel.com
> > > > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > > > Cc: Pekka Paalanen <ppaalanen@gmail.com>
> > > > Cc: Daniel Stone <daniel@fooishbar.org>
> > > > Cc: intel-gfx@lists.freedesktop.org
> > > > Cc: igt-dev@lists.freedesktop.org
> > > > Cc: petri.latvala@intel.com
> > > > Cc: chris@chris-wilson.co.uk
> > > > Signed-off-by: Deepak Rawat <drawat@vmware.com>
> > > > ---
> > > > drivers/gpu/drm/selftests/Makefile | 3 +-
> > > > .../selftests/drm_damage_helper_selftests.h | 22 +
> > > > .../drm/selftests/test-drm_damage_helper.c | 844
> > > ++++++++++++++++++
> > > > 3 files changed, 868 insertions(+), 1 deletion(-)
> > > > create mode 100644
> > > drivers/gpu/drm/selftests/drm_damage_helper_selftests.h
> > > > create mode 100644 drivers/gpu/drm/selftests/test-
> > > drm_damage_helper.c
> > > >
> > > > diff --git a/drivers/gpu/drm/selftests/Makefile
> > > b/drivers/gpu/drm/selftests/Makefile
> > > > index 9fc349fa18e9..88ac216f5962 100644
> > > > --- a/drivers/gpu/drm/selftests/Makefile
> > > > +++ b/drivers/gpu/drm/selftests/Makefile
> > > > @@ -1 +1,2 @@
> > > > -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm-
> > > helper.o
> > > > +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm-
> > > helper.o \
> > > > + test-drm_damage_helper.o
> > >
> > > With the testcase intagrated into the test-drm-helper.ko module, for
> > > patches 1-4 in this series:
> > >
> > > Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> > >
> > > Obviously needs some adjusting on the igt side too, since we seem to be
> > > missing the igt scaffolding for tests-drm-helper.ko.
> > > -Daniel
> >
> > Hi Daniel,
> >
> > Thanks for the review. I am a little confused here. Should we have single
> > kernel module for drm plane helper selftest and damage helper selftest?
> > Also shall I rename the kernel selfttest to kms_*?
> >
> > For user-space igt test it should be it makes sense to rename to kms_selftets?
>
> Since I went back&forth on this way too many times:
> - igt should be called kms_selftest. Please work together with igt
> maintainers (Arek and Petri), since we also need to update the CI
> building infrastructure to make sure it updates the list of subtests
> implemented by the kernel.
>
> - Kernel module I'd call test-drm_modeset.ko. That kernel module can then
> include the existing test-drm-helper.c (could probably rename to
> test-drm_plane_helper.c for clarity) and your new damage helper (named
> test-drm_damage_helper.c for consistency).
>
> Does that make sense to everyone?
I was trying to add some selftests, as well here [1], with that in
mind, I think it makes sense to have just one module, call it
"test-drm_modeset" or whatever and separate the tests source code base
on whatever core functionality they are testing.
Besides compiling everything together, probably some stuff will have
to move out of test-drm-helper.c into some common header. For example
this "FAIL/FAIL_ON" macros
[1] https://lists.freedesktop.org/archives/dri-devel/2018-October/192973.html
>
> Thanks, Daniel
>
> >
> > >
> > > > diff --git a/drivers/gpu/drm/selftests/drm_damage_helper_selftests.h
> > > b/drivers/gpu/drm/selftests/drm_damage_helper_selftests.h
> > > > new file mode 100644
> > > > index 000000000000..3a1cbe05bef0
> > > > --- /dev/null
> > > > +++ b/drivers/gpu/drm/selftests/drm_damage_helper_selftests.h
> > > > @@ -0,0 +1,22 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > > +selftest(damage_iter_no_damage, igt_damage_iter_no_damage)
> > > > +selftest(damage_iter_no_damage_fractional_src,
> > > igt_damage_iter_no_damage_fractional_src)
> > > > +selftest(damage_iter_no_damage_src_moved,
> > > igt_damage_iter_no_damage_src_moved)
> > > > +selftest(damage_iter_no_damage_fractional_src_moved,
> > > igt_damage_iter_no_damage_fractional_src_moved)
> > > > +selftest(damage_iter_no_damage_not_visible,
> > > igt_damage_iter_no_damage_not_visible)
> > > > +selftest(damage_iter_no_damage_no_crtc,
> > > igt_damage_iter_no_damage_no_crtc)
> > > > +selftest(damage_iter_no_damage_no_fb,
> > > igt_damage_iter_no_damage_no_fb)
> > > > +selftest(damage_iter_simple_damage,
> > > igt_damage_iter_simple_damage)
> > > > +selftest(damage_iter_single_damage, igt_damage_iter_single_damage)
> > > > +selftest(damage_iter_single_damage_intersect_src,
> > > igt_damage_iter_single_damage_intersect_src)
> > > > +selftest(damage_iter_single_damage_outside_src,
> > > igt_damage_iter_single_damage_outside_src)
> > > > +selftest(damage_iter_single_damage_fractional_src,
> > > igt_damage_iter_single_damage_fractional_src)
> > > > +selftest(damage_iter_single_damage_intersect_fractional_src,
> > > igt_damage_iter_single_damage_intersect_fractional_src)
> > > > +selftest(damage_iter_single_damage_outside_fractional_src,
> > > igt_damage_iter_single_damage_outside_fractional_src)
> > > > +selftest(damage_iter_single_damage_src_moved,
> > > igt_damage_iter_single_damage_src_moved)
> > > > +selftest(damage_iter_single_damage_fractional_src_moved,
> > > igt_damage_iter_single_damage_fractional_src_moved)
> > > > +selftest(damage_iter_damage, igt_damage_iter_damage)
> > > > +selftest(damage_iter_damage_one_intersect,
> > > igt_damage_iter_damage_one_intersect)
> > > > +selftest(damage_iter_damage_one_outside,
> > > igt_damage_iter_damage_one_outside)
> > > > +selftest(damage_iter_damage_src_moved,
> > > igt_damage_iter_damage_src_moved)
> > > > +selftest(damage_iter_damage_not_visible,
> > > igt_damage_iter_damage_not_visible)
> > > > diff --git a/drivers/gpu/drm/selftests/test-drm_damage_helper.c
> > > b/drivers/gpu/drm/selftests/test-drm_damage_helper.c
> > > > new file mode 100644
> > > > index 000000000000..17754734c47a
> > > > --- /dev/null
> > > > +++ b/drivers/gpu/drm/selftests/test-drm_damage_helper.c
> > > > @@ -0,0 +1,844 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > > +/*
> > > > + * Test case for drm_damage_helper functions
> > > > + */
> > > > +
> > > > +#define pr_fmt(fmt) "drm_damage_helper: " fmt
> > > > +
> > > > +#include <linux/module.h>
> > > > +#include <drm/drm_damage_helper.h>
> > > > +
> > > > +#define TESTS "drm_damage_helper_selftests.h"
> > > > +#include "drm_selftest.h"
> > > > +
> > > > +#define FAIL(test, msg, ...) \
> > > > + do { \
> > > > + if (test) { \
> > > > + pr_err("%s/%u: " msg, __FUNCTION__, __LINE__,
> > > ##__VA_ARGS__); \
> > > > + return -EINVAL; \
> > > > + } \
> > > > + } while (0)
> > > > +
> > > > +#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n")
> > > > +
> > > > +static void set_plane_src(struct drm_plane_state *state, int x1, int y1, int
> > > x2,
> > > > + int y2)
> > > > +{
> > > > + state->src.x1 = x1;
> > > > + state->src.y1 = y1;
> > > > + state->src.x2 = x2;
> > > > + state->src.y2 = y2;
> > > > +}
> > > > +
> > > > +static void set_damage_clip(struct drm_mode_rect *r, int x1, int y1, int
> > > x2,
> > > > + int y2)
> > > > +{
> > > > + r->x1 = x1;
> > > > + r->y1 = y1;
> > > > + r->x2 = x2;
> > > > + r->y2 = y2;
> > > > +}
> > > > +
> > > > +static void set_damage_blob(struct drm_property_blob *damage_blob,
> > > > + struct drm_mode_rect *r, uint32_t size)
> > > > +{
> > > > + damage_blob->length = size;
> > > > + damage_blob->data = r;
> > > > +}
> > > > +
> > > > +static void set_plane_damage(struct drm_plane_state *state,
> > > > + struct drm_property_blob *damage_blob)
> > > > +{
> > > > + state->fb_damage_clips = damage_blob;
> > > > +}
> > > > +
> > > > +static bool check_damage_clip(struct drm_plane_state *state, struct
> > > drm_rect *r,
> > > > + int x1, int y1, int x2, int y2)
> > > > +{
> > > > + /*
> > > > + * Round down x1/y1 and round up x2/y2. This is because damage is
> > > not in
> > > > + * 16.16 fixed point so to catch all pixels.
> > > > + */
> > > > + int src_x1 = state->src.x1 >> 16;
> > > > + int src_y1 = state->src.y1 >> 16;
> > > > + int src_x2 = (state->src.x2 >> 16) + !!(state->src.x2 & 0xFFFF);
> > > > + int src_y2 = (state->src.y2 >> 16) + !!(state->src.y2 & 0xFFFF);
> > > > +
> > > > + if (x1 >= x2 || y1 >= y2) {
> > > > + pr_err("Cannot have damage clip with no dimention.\n");
> > > > + return false;
> > > > + }
> > > > +
> > > > + if (x1 < src_x1 || y1 < src_y1 || x2 > src_x2 || y2 > src_y2) {
> > > > + pr_err("Damage cannot be outside rounded plane src.\n");
> > > > + return false;
> > > > + }
> > > > +
> > > > + if (r->x1 != x1 || r->y1 != y1 || r->x2 != x2 || r->y2 != y2) {
> > > > + pr_err("Damage = %d %d %d %d\n", r->x1, r->y1, r->x2, r-
> > > >y2);
> > > > + return false;
> > > > + }
> > > > +
> > > > + return true;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src same as fb size. */
> > > > + set_plane_src(&old_state, 0, 0, fb.width << 16, fb.height << 16);
> > > > + set_plane_src(&state, 0, 0, fb.width << 16, fb.height << 16);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return plane src as damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 0, 0, 2048, 2048));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage_fractional_src(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src has fractional part. */
> > > > + set_plane_src(&old_state, 0x3fffe, 0x3fffe,
> > > > + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
> > > > + set_plane_src(&state, 0x3fffe, 0x3fffe,
> > > > + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return rounded off plane src as
> > > damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 3, 3, 1028, 772));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage_src_moved(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src moved since old plane state. */
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 10 << 16, 10 << 16,
> > > > + (10 + 1024) << 16, (10 + 768) << 16);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return plane src as damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 1034, 778));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage_fractional_src_moved(void
> > > *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src has fractional part and it moved since old plane state. */
> > > > + set_plane_src(&old_state, 0x3fffe, 0x3fffe,
> > > > + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
> > > > + set_plane_src(&state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return plane src as damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage_not_visible(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = false,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 0, "Should have no damage.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage_no_crtc(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = 0,
> > > > + .fb = &fb,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 0, "Should have no damage.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_no_damage_no_fb(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = 0,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 0, "Should have no damage.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_simple_damage(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + /* Damage set to plane src */
> > > > + set_damage_clip(&damage, 0, 0, 1024, 768);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return damage when set.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 0, 0, 1024, 768));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_damage_clip(&damage, 256, 192, 768, 576);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return damage when set.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 256, 192, 768, 576));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_intersect_src(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + /* Damage intersect with plane src. */
> > > > + set_damage_clip(&damage, 256, 192, 1360, 768);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return damage clipped to src.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 256, 192, 1024, 768));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_outside_src(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + /* Damage clip outside plane src */
> > > > + set_damage_clip(&damage, 1360, 1360, 1380, 1380);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 0, "Should have no damage.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_fractional_src(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src has fractional part. */
> > > > + set_plane_src(&old_state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_plane_src(&state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_damage_clip(&damage, 10, 10, 256, 330);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return damage when set.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 256, 330));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_intersect_fractional_src(void
> > > *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src has fractional part. */
> > > > + set_plane_src(&old_state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_plane_src(&state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + /* Damage intersect with plane src. */
> > > > + set_damage_clip(&damage, 10, 1, 1360, 330);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return damage clipped to rounded off
> > > src.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 10, 4, 1029, 330));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_outside_fractional_src(void
> > > *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src has fractional part. */
> > > > + set_plane_src(&old_state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_plane_src(&state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + /* Damage clip outside plane src */
> > > > + set_damage_clip(&damage, 1360, 1360, 1380, 1380);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 0, "Should have no damage.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_src_moved(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src moved since old plane state. */
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 10 << 16, 10 << 16,
> > > > + (10 + 1024) << 16, (10 + 768) << 16);
> > > > + set_damage_clip(&damage, 20, 30, 256, 256);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return plane src as damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 1034, 778));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_single_damage_fractional_src_moved(void
> > > *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage;
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + /* Plane src with fractional part moved since old plane state. */
> > > > + set_plane_src(&old_state, 0x3fffe, 0x3fffe,
> > > > + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
> > > > + set_plane_src(&state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + /* Damage intersect with plane src. */
> > > > + set_damage_clip(&damage, 20, 30, 1360, 256);
> > > > + set_damage_blob(&damage_blob, &damage, sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return rounded off plane src as
> > > damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_damage(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage[2];
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + /* 2 damage clips. */
> > > > + set_damage_clip(&damage[0], 20, 30, 200, 180);
> > > > + set_damage_clip(&damage[1], 240, 200, 280, 250);
> > > > + set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip) {
> > > > + if (num_hits == 0)
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 20, 30,
> > > 200, 180));
> > > > + if (num_hits == 1)
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 240,
> > > 200, 280, 250));
> > > > + num_hits++;
> > > > + }
> > > > +
> > > > + FAIL(num_hits != 2, "Should return damage when set.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_damage_one_intersect(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage[2];
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_plane_src(&state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + /* 2 damage clips, one intersect plane src. */
> > > > + set_damage_clip(&damage[0], 20, 30, 200, 180);
> > > > + set_damage_clip(&damage[1], 2, 2, 1360, 1360);
> > > > + set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip) {
> > > > + if (num_hits == 0)
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 20, 30,
> > > 200, 180));
> > > > + if (num_hits == 1)
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 4, 4,
> > > 1029, 773));
> > > > + num_hits++;
> > > > + }
> > > > +
> > > > + FAIL(num_hits != 2, "Should return damage when set.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_damage_one_outside(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage[2];
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
> > > > + set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
> > > > + /* 2 damage clips, one outside plane src. */
> > > > + set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
> > > > + set_damage_clip(&damage[1], 240, 200, 280, 250);
> > > > + set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return damage when set.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 240, 200, 280, 250));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_damage_src_moved(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage[2];
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = true,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_plane_src(&state, 0x3fffe, 0x3fffe,
> > > > + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
> > > > + /* 2 damage clips, one outside plane src. */
> > > > + set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
> > > > + set_damage_clip(&damage[1], 240, 200, 280, 250);
> > > > + set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 1, "Should return round off plane src as damage.");
> > > > + FAIL_ON(!check_damage_clip(&state, &clip, 3, 3, 1028, 772));
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int igt_damage_iter_damage_not_visible(void *ignored)
> > > > +{
> > > > + struct drm_atomic_helper_damage_iter iter;
> > > > + struct drm_plane_state old_state;
> > > > + struct drm_property_blob damage_blob;
> > > > + struct drm_mode_rect damage[2];
> > > > + struct drm_rect clip;
> > > > + uint32_t num_hits = 0;
> > > > +
> > > > + struct drm_framebuffer fb = {
> > > > + .width = 2048,
> > > > + .height = 2048
> > > > + };
> > > > +
> > > > + struct drm_plane_state state = {
> > > > + .crtc = ZERO_SIZE_PTR,
> > > > + .fb = &fb,
> > > > + .visible = false,
> > > > + };
> > > > +
> > > > + set_plane_src(&old_state, 0x40002, 0x40002,
> > > > + 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
> > > > + set_plane_src(&state, 0x3fffe, 0x3fffe,
> > > > + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
> > > > + /* 2 damage clips, one outside plane src. */
> > > > + set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
> > > > + set_damage_clip(&damage[1], 240, 200, 280, 250);
> > > > + set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
> > > > + set_plane_damage(&state, &damage_blob);
> > > > + drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
> > > > + drm_atomic_for_each_plane_damage(&iter, &clip)
> > > > + num_hits++;
> > > > +
> > > > + FAIL(num_hits != 0, "Should not return any damage.");
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +#include "drm_selftest.c"
> > > > +
> > > > +static int __init test_drm_damage_helper_init(void)
> > > > +{
> > > > + int err;
> > > > +
> > > > + err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL);
> > > > +
> > > > + return err > 0 ? 0 : err;
> > > > +}
> > > > +
> > > > +static void __exit test_drm_damage_helper_exit(void)
> > > > +{
> > > > +}
> > > > +
> > > > +module_init(test_drm_damage_helper_init);
> > > > +module_exit(test_drm_damage_helper_exit);
> > > > +
> > > > +MODULE_AUTHOR("VMware Inc.");
> > > > +MODULE_LICENSE("GPL");
> > > > --
> > > > 2.17.1
> > > >
> > >
> > > --
> > > Daniel Vetter
> > > Software Engineer, Intel Corporation
> > > https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fblog.ff
> > > wll.ch&data=02%7C01%7Cdrawat%40vmware.com%7C64e7deb3dc264c
> > > 6f11a008d62f95d96e%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C1%7C0%7
> > > C636748717979714197&sdata=vCz71L%2BoMpFSGFZifg%2F0bR1CxzReLa
> > > 7Dy3ss3UKeLrU%3D&reserved=0
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Cheers,
Alex G
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
next prev parent reply other threads:[~2018-10-16 12:52 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20181011001657.1715-1-drawat@vmware.com>
2018-10-11 0:16 ` [Intel-gfx] [PATCH v3 04/18] drm/selftest: Add drm damage helper selftest Deepak Rawat
2018-10-11 16:23 ` Daniel Vetter
2018-10-15 16:11 ` Deepak Singh Rawat
2018-10-16 12:21 ` [Intel-gfx] " Daniel Vetter
2018-10-16 12:52 ` Alexandru-Cosmin Gheorghe [this message]
2018-10-16 16:14 ` Deepak Singh Rawat
2018-10-16 16:00 ` [Intel-gfx] " Deepak Singh Rawat
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=20181016125208.GA21819@e114479-lin.cambridge.arm.com \
--to=alexandru-cosmin.gheorghe@arm.com \
--cc=daniel.vetter@ffwll.ch \
--cc=daniel@ffwll.ch \
--cc=drawat@vmware.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=igt-dev@lists.freedesktop.org \
--cc=intel-gfx@lists.freedesktop.org \
--cc=linux-graphics-maintainer@vmware.com \
--cc=nd@arm.com \
--cc=petri.latvala@intel.com \
--cc=thellstrom@vmware.com \
/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