summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/kms_chamelium.c438
1 files changed, 438 insertions, 0 deletions
diff --git a/tests/kms_chamelium.c b/tests/kms_chamelium.c
index f332592b..e0715950 100644
--- a/tests/kms_chamelium.c
+++ b/tests/kms_chamelium.c
@@ -26,6 +26,7 @@
#include "config.h"
#include "igt.h"
+#include "igt_vc4.h"
#include <fcntl.h>
#include <string.h>
@@ -703,6 +704,435 @@ test_display_frame_dump(data_t *data, struct chamelium_port *port)
drmModeFreeConnector(connector);
}
+static void select_tiled_modifier(igt_plane_t *plane, uint32_t width,
+ uint32_t height, uint32_t format,
+ uint64_t *modifier)
+{
+ if (igt_plane_has_format_mod(plane, format,
+ DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED)) {
+ igt_debug("Selecting VC4 T-tiling\n");
+
+ *modifier = DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED;
+ } else if (igt_plane_has_format_mod(plane, format,
+ DRM_FORMAT_MOD_BROADCOM_SAND256)) {
+ /* Randomize the column height to less than twice the minimum. */
+ size_t column_height = (rand() % height) + height;
+
+ igt_debug("Selecting VC4 SAND256 tiling with column height %ld\n",
+ column_height);
+
+ *modifier = DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(column_height);
+ } else {
+ *modifier = DRM_FORMAT_MOD_LINEAR;
+ }
+}
+
+static void randomize_plane_format_stride(igt_plane_t *plane,
+ uint32_t width, uint32_t height,
+ uint32_t *format, uint64_t *modifier,
+ size_t *stride, bool allow_yuv)
+{
+ size_t stride_min;
+ uint32_t *formats_array;
+ unsigned int formats_count;
+ unsigned int count = 0;
+ unsigned int i;
+ bool tiled;
+ int index;
+
+ igt_format_array_fill(&formats_array, &formats_count, allow_yuv);
+
+ /* First pass to count the supported formats. */
+ for (i = 0; i < formats_count; i++)
+ if (igt_plane_has_format_mod(plane, formats_array[i],
+ DRM_FORMAT_MOD_LINEAR))
+ count++;
+
+ igt_assert(count > 0);
+
+ index = rand() % count;
+
+ /* Second pass to get the index-th supported format. */
+ for (i = 0; i < formats_count; i++) {
+ if (!igt_plane_has_format_mod(plane, formats_array[i],
+ DRM_FORMAT_MOD_LINEAR))
+ continue;
+
+ if (!index--) {
+ *format = formats_array[i];
+ break;
+ }
+ }
+
+ free(formats_array);
+
+ igt_assert(index < 0);
+
+ stride_min = width * igt_format_plane_bpp(*format, 0) / 8;
+
+ /* Randomize the stride to less than twice the minimum. */
+ *stride = (rand() % stride_min) + stride_min;
+
+ /* Pixman requires the stride to be aligned to 32-byte words. */
+ *stride = ALIGN(*stride, sizeof(uint32_t));
+
+ /* Randomize the use of a tiled mode with a 1/4 probability. */
+ tiled = ((rand() % 4) == 0);
+
+ if (tiled)
+ select_tiled_modifier(plane, width, height, *format, modifier);
+ else
+ *modifier = DRM_FORMAT_MOD_LINEAR;
+}
+
+static void randomize_plane_dimensions(drmModeModeInfo *mode,
+ uint32_t *width, uint32_t *height,
+ uint32_t *src_w, uint32_t *src_h,
+ uint32_t *src_x, uint32_t *src_y,
+ uint32_t *crtc_w, uint32_t *crtc_h,
+ int32_t *crtc_x, int32_t *crtc_y,
+ bool allow_scaling)
+{
+ double ratio;
+
+ /* Randomize width and height in the mode dimensions range. */
+ *width = (rand() % mode->hdisplay) + 1;
+ *height = (rand() % mode->vdisplay) + 1;
+
+ /* Randomize source offset in the first half of the original size. */
+ *src_x = rand() % (*width / 2);
+ *src_y = rand() % (*height / 2);
+
+ /* The source size only includes the active source area. */
+ *src_w = *width - *src_x;
+ *src_h = *height - *src_y;
+
+ if (allow_scaling) {
+ *crtc_w = (rand() % mode->hdisplay) + 1;
+ *crtc_h = (rand() % mode->vdisplay) + 1;
+
+ /*
+ * Don't bother with scaling if dimensions are quite close in
+ * order to get non-scaling cases more frequently. Also limit
+ * scaling to 3x to avoid agressive filtering that makes
+ * comparison less reliable.
+ */
+
+ ratio = ((double) *crtc_w / *src_w);
+ if (ratio > 0.8 && ratio < 1.2)
+ *crtc_w = *src_w;
+ else if (ratio > 3.0)
+ *crtc_w = *src_w * 3;
+
+ ratio = ((double) *crtc_h / *src_h);
+ if (ratio > 0.8 && ratio < 1.2)
+ *crtc_h = *src_h;
+ else if (ratio > 3.0)
+ *crtc_h = *src_h * 3;
+ } else {
+ *crtc_w = *src_w;
+ *crtc_h = *src_h;
+ }
+
+ if (*crtc_w != *src_w || *crtc_h != *src_h) {
+ /*
+ * When scaling is involved, make sure to not go off-bounds or
+ * scaled clipping may result in decimal dimensions, that most
+ * drivers don't support.
+ */
+ *crtc_x = rand() % (mode->hdisplay - *crtc_w);
+ *crtc_y = rand() % (mode->vdisplay - *crtc_h);
+ } else {
+ /*
+ * Randomize the on-crtc position and allow the plane to go
+ * off-display by less than half of its on-crtc dimensions.
+ */
+ *crtc_x = (rand() % mode->hdisplay) - *crtc_w / 2;
+ *crtc_y = (rand() % mode->vdisplay) - *crtc_h / 2;
+ }
+}
+
+static void blit_plane_cairo(data_t *data, cairo_surface_t *result,
+ uint32_t src_w, uint32_t src_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t crtc_w, uint32_t crtc_h,
+ int32_t crtc_x, int32_t crtc_y,
+ struct igt_fb *fb)
+{
+ cairo_surface_t *surface;
+ cairo_surface_t *clipped_surface;
+ cairo_t *cr;
+
+ surface = igt_get_cairo_surface(data->drm_fd, fb);
+
+ if (src_x || src_y) {
+ clipped_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+ src_w, src_h);
+
+ cr = cairo_create(clipped_surface);
+
+ cairo_translate(cr, -1. * src_x, -1. * src_y);
+
+ cairo_set_source_surface(cr, surface, 0, 0);
+
+ cairo_paint(cr);
+ cairo_surface_flush(clipped_surface);
+
+ cairo_destroy(cr);
+ } else {
+ clipped_surface = surface;
+ }
+
+ cr = cairo_create(result);
+
+ cairo_translate(cr, crtc_x, crtc_y);
+
+ if (src_w != crtc_w || src_h != crtc_h) {
+ cairo_scale(cr, (double) crtc_w / src_w,
+ (double) crtc_h / src_h);
+ }
+
+ cairo_set_source_surface(cr, clipped_surface, 0, 0);
+ cairo_surface_destroy(clipped_surface);
+
+ if (src_w != crtc_w || src_h != crtc_h) {
+ cairo_pattern_set_filter(cairo_get_source(cr),
+ CAIRO_FILTER_BILINEAR);
+ cairo_pattern_set_extend(cairo_get_source(cr),
+ CAIRO_EXTEND_NONE);
+ }
+
+ cairo_paint(cr);
+ cairo_surface_flush(result);
+
+ cairo_destroy(cr);
+}
+
+static void configure_plane(igt_plane_t *plane, uint32_t src_w, uint32_t src_h,
+ uint32_t src_x, uint32_t src_y, uint32_t crtc_w,
+ uint32_t crtc_h, int32_t crtc_x, int32_t crtc_y,
+ struct igt_fb *fb)
+{
+ igt_plane_set_fb(plane, fb);
+
+ igt_plane_set_position(plane, crtc_x, crtc_y);
+ igt_plane_set_size(plane, crtc_w, crtc_h);
+
+ igt_fb_set_position(fb, plane, src_x, src_y);
+ igt_fb_set_size(fb, plane, src_w, src_h);
+}
+
+static void prepare_randomized_plane(data_t *data,
+ drmModeModeInfo *mode,
+ igt_plane_t *plane,
+ struct igt_fb *overlay_fb,
+ unsigned int index,
+ cairo_surface_t *result_surface,
+ bool allow_scaling, bool allow_yuv)
+{
+ struct igt_fb pattern_fb;
+ uint32_t overlay_fb_w, overlay_fb_h;
+ uint32_t overlay_src_w, overlay_src_h;
+ uint32_t overlay_src_x, overlay_src_y;
+ int32_t overlay_crtc_x, overlay_crtc_y;
+ uint32_t overlay_crtc_w, overlay_crtc_h;
+ uint32_t format;
+ uint64_t modifier;
+ size_t stride;
+ bool tiled;
+ int fb_id;
+
+ randomize_plane_dimensions(mode, &overlay_fb_w, &overlay_fb_h,
+ &overlay_src_w, &overlay_src_h,
+ &overlay_src_x, &overlay_src_y,
+ &overlay_crtc_w, &overlay_crtc_h,
+ &overlay_crtc_x, &overlay_crtc_y,
+ allow_scaling);
+
+ igt_debug("Plane %d: framebuffer size %dx%d\n", index,
+ overlay_fb_w, overlay_fb_h);
+ igt_debug("Plane %d: on-crtc size %dx%d\n", index,
+ overlay_crtc_w, overlay_crtc_h);
+ igt_debug("Plane %d: on-crtc position %dx%d\n", index,
+ overlay_crtc_x, overlay_crtc_y);
+ igt_debug("Plane %d: in-framebuffer size %dx%d\n", index,
+ overlay_src_w, overlay_src_h);
+ igt_debug("Plane %d: in-framebuffer position %dx%d\n", index,
+ overlay_src_x, overlay_src_y);
+
+ /* Get a pattern framebuffer for the overlay plane. */
+ fb_id = chamelium_get_pattern_fb(data, overlay_fb_w, overlay_fb_h,
+ DRM_FORMAT_XRGB8888, 32, &pattern_fb);
+ igt_assert(fb_id > 0);
+
+ randomize_plane_format_stride(plane, overlay_fb_w, overlay_fb_h,
+ &format, &modifier, &stride, allow_yuv);
+
+ tiled = (modifier != LOCAL_DRM_FORMAT_MOD_NONE);
+
+ igt_debug("Plane %d: %s format (%s) with stride %ld\n", index,
+ igt_format_str(format), tiled ? "tiled" : "linear", stride);
+
+ fb_id = igt_fb_convert_with_stride(overlay_fb, &pattern_fb, format,
+ modifier, stride);
+ igt_assert(fb_id > 0);
+
+ blit_plane_cairo(data, result_surface, overlay_src_w, overlay_src_h,
+ overlay_src_x, overlay_src_y,
+ overlay_crtc_w, overlay_crtc_h,
+ overlay_crtc_x, overlay_crtc_y, &pattern_fb);
+
+ configure_plane(plane, overlay_src_w, overlay_src_h,
+ overlay_src_x, overlay_src_y,
+ overlay_crtc_w, overlay_crtc_h,
+ overlay_crtc_x, overlay_crtc_y, overlay_fb);
+
+ /* Remove the original pattern framebuffer. */
+ igt_remove_fb(data->drm_fd, &pattern_fb);
+}
+
+static void test_display_planes_random(data_t *data,
+ struct chamelium_port *port,
+ enum chamelium_check check)
+{
+ igt_output_t *output;
+ drmModeModeInfo *mode;
+ igt_plane_t *primary_plane;
+ struct igt_fb primary_fb;
+ struct igt_fb result_fb;
+ struct igt_fb *overlay_fbs;
+ igt_crc_t *crc;
+ igt_crc_t *expected_crc;
+ struct chamelium_fb_crc_async_data *fb_crc;
+ unsigned int overlay_planes_max = 0;
+ unsigned int overlay_planes_count;
+ cairo_surface_t *result_surface;
+ int captured_frame_count;
+ bool allow_scaling;
+ bool allow_yuv;
+ unsigned int i;
+ unsigned int fb_id;
+
+ switch (check) {
+ case CHAMELIUM_CHECK_CRC:
+ allow_scaling = false;
+ allow_yuv = false;
+ break;
+ case CHAMELIUM_CHECK_CHECKERBOARD:
+ allow_scaling = true;
+ allow_yuv = true;
+ break;
+ default:
+ igt_assert(false);
+ }
+
+ srand(time(NULL));
+
+ reset_state(data, port);
+
+ /* Find the connector and pipe. */
+ output = prepare_output(data, port);
+
+ mode = igt_output_get_mode(output);
+
+ /* Get a framebuffer for the primary plane. */
+ primary_plane = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+ igt_assert(primary_plane);
+
+ fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay,
+ DRM_FORMAT_XRGB8888, 64, &primary_fb);
+ igt_assert(fb_id > 0);
+
+ /* Get a framebuffer for the cairo composition result. */
+ fb_id = igt_create_fb(data->drm_fd, mode->hdisplay,
+ mode->vdisplay, DRM_FORMAT_XRGB8888,
+ LOCAL_DRM_FORMAT_MOD_NONE, &result_fb);
+ igt_assert(fb_id > 0);
+
+ result_surface = igt_get_cairo_surface(data->drm_fd, &result_fb);
+
+ /* Paint the primary framebuffer on the result surface. */
+ blit_plane_cairo(data, result_surface, 0, 0, 0, 0, 0, 0, 0, 0,
+ &primary_fb);
+
+ /* Configure the primary plane. */
+ igt_plane_set_fb(primary_plane, &primary_fb);
+
+ overlay_planes_max =
+ igt_output_count_plane_type(output, DRM_PLANE_TYPE_OVERLAY);
+
+ /* Limit the number of planes to a reasonable scene. */
+ overlay_planes_max = max(overlay_planes_max, 4);
+
+ overlay_planes_count = (rand() % overlay_planes_max) + 1;
+ igt_debug("Using %d overlay planes\n", overlay_planes_count);
+
+ overlay_fbs = calloc(sizeof(struct igt_fb), overlay_planes_count);
+
+ for (i = 0; i < overlay_planes_count; i++) {
+ struct igt_fb *overlay_fb = &overlay_fbs[i];
+ igt_plane_t *plane =
+ igt_output_get_plane_type_index(output,
+ DRM_PLANE_TYPE_OVERLAY,
+ i);
+ igt_assert(plane);
+
+ prepare_randomized_plane(data, mode, plane, overlay_fb, i,
+ result_surface, allow_scaling,
+ allow_yuv);
+ }
+
+ cairo_surface_destroy(result_surface);
+
+ if (check == CHAMELIUM_CHECK_CRC)
+ fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd,
+ &result_fb);
+
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+ if (check == CHAMELIUM_CHECK_CRC) {
+ chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1);
+ crc = chamelium_read_captured_crcs(data->chamelium,
+ &captured_frame_count);
+
+ igt_assert(captured_frame_count == 1);
+
+ expected_crc = chamelium_calculate_fb_crc_async_finish(fb_crc);
+
+ chamelium_assert_crc_eq_or_dump(data->chamelium,
+ expected_crc, crc,
+ &result_fb, 0);
+
+ free(expected_crc);
+ free(crc);
+ } else if (check == CHAMELIUM_CHECK_CHECKERBOARD) {
+ struct chamelium_frame_dump *dump;
+
+ dump = chamelium_port_dump_pixels(data->chamelium, port, 0, 0,
+ 0, 0);
+ chamelium_assert_frame_match_or_dump(data->chamelium, port,
+ dump, &result_fb, check);
+ chamelium_destroy_frame_dump(dump);
+ }
+
+ for (i = 0; i < overlay_planes_count; i++) {
+ struct igt_fb *overlay_fb = &overlay_fbs[i];
+ igt_plane_t *plane;
+
+ plane = igt_output_get_plane_type_index(output,
+ DRM_PLANE_TYPE_OVERLAY,
+ i);
+ igt_assert(plane);
+
+ igt_remove_fb(data->drm_fd, overlay_fb);
+ }
+
+ free(overlay_fbs);
+
+ igt_remove_fb(data->drm_fd, &primary_fb);
+ igt_remove_fb(data->drm_fd, &result_fb);
+}
+
static void
test_hpd_without_ddc(data_t *data, struct chamelium_port *port)
{
@@ -977,6 +1407,10 @@ igt_main
test_display_one_mode(&data, port, DRM_FORMAT_XRGB1555,
CHAMELIUM_CHECK_CRC, 1);
+ connector_subtest("hdmi-crc-planes-random", HDMIA)
+ test_display_planes_random(&data, port,
+ CHAMELIUM_CHECK_CRC);
+
connector_subtest("hdmi-cmp-nv12", HDMIA)
test_display_one_mode(&data, port, DRM_FORMAT_NV12,
CHAMELIUM_CHECK_CHECKERBOARD, 1);
@@ -1009,6 +1443,10 @@ igt_main
test_display_one_mode(&data, port, DRM_FORMAT_YVU422,
CHAMELIUM_CHECK_CHECKERBOARD, 1);
+ connector_subtest("hdmi-cmp-planes-random", HDMIA)
+ test_display_planes_random(&data, port,
+ CHAMELIUM_CHECK_CHECKERBOARD);
+
connector_subtest("hdmi-frame-dump", HDMIA)
test_display_frame_dump(&data, port);
}