add nv12_test

BUG=chrome-os-partner:41644
TEST=run nv12_test

Change-Id: I03d4a70053d107a566aeca361682c0787e7cdcc0
Signed-off-by: Dominik Behr <dbehr@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/284925
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..1d83aaf
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+
+#########################
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES :=  nv12_test.c bo.c dev.c modeset.c
+
+LOCAL_MODULE := nv12_test
+
+LOCAL_C_INCLUDES := \
+	external/libdrm \
+	external/libdrm/include/drm
+LOCAL_CFLAGS := -O2 -g -W -Wall
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc
+LOCAL_SHARED_LIBRARIES := libdrm
+
+include $(BUILD_EXECUTABLE)
diff --git a/Makefile b/Makefile
index f893787..55e82ff 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@
 CFLAGS += $(PC_CFLAGS)
 LDLIBS += $(PC_LIBS)
 
-all: CC_BINARY(null_platform_test) CC_BINARY(vgem_test) CC_BINARY(vgem_fb_test) CC_BINARY(swrast_test) CC_BINARY(atomictest) CC_BINARY(gamma_test)
+all: CC_BINARY(null_platform_test) CC_BINARY(vgem_test) CC_BINARY(vgem_fb_test) CC_BINARY(swrast_test) CC_BINARY(atomictest) CC_BINARY(gamma_test) CC_BINARY(nv12_test)
 
 CC_BINARY(null_platform_test): null_platform_test.o
 CC_BINARY(null_platform_test): LDLIBS += $(DRM_LIBS)
@@ -32,3 +32,6 @@
 
 CC_BINARY(gamma_test): gamma_test.o dev.o bo.o modeset.o
 CC_BINARY(gamma_test): LDLIBS += -lm $(DRM_LIBS)
+
+CC_BINARY(nv12_test): nv12_test.o dev.o bo.o modeset.o
+CC_BINARY(nv12_test): LDLIBS += -lm $(DRM_LIBS)
diff --git a/bo.c b/bo.c
index 3d2155f..9a0b14d 100644
--- a/bo.c
+++ b/bo.c
@@ -19,34 +19,58 @@
 	draw_rect(bo, 0, 0, bo->width, bo->height, a, r, g, b);
 }
 
+static uint8_t clampbyte(float f)
+{
+	if (f >= 255.0) return 255;
+	if (f <= 0.0) return 0;
+	return (uint8_t)f;
+}
+
 void draw_rect(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
 		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
 {
 	uint32_t i, j, xmax = x + width, ymax = y + height;
+	uint8_t Y = 0, Cb = 0, Cr = 0;
 
 	if (xmax > bo->width)
 		xmax = bo->width;
 	if (ymax > bo->height)
 		ymax = bo->height;
 
+	if (bo->format == DRM_FORMAT_NV12 ||
+	    bo->format == DRM_FORMAT_NV21) {
+		Y = clampbyte(16 + 0.2567890625 * r + 0.50412890625 * g * 0.09790625);
+		Cb = clampbyte(128 - 0.14822265625 * r - 0.2909921875 * g + 0.43921484375 * b);
+		Cr = clampbyte(128 + 0.43921484375 * r - 0.3677890625 * g - 0.07142578125 * b);
+	}
+
 	for (i = y; i < ymax; i++) {
 		uint8_t *row = bo->map_addr + i * bo->pitch;
+		uint8_t *uvrow = bo->map_addr + bo->height * bo->pitch + (i >> 1) * bo->pitch;
 
 		for (j = x; j < xmax; j++) {
-			uint8_t *pixel = row + j * 4;
-
 			if (bo->format == DRM_FORMAT_ARGB8888 ||
 			    bo->format == DRM_FORMAT_XRGB8888)
 			{
+				uint8_t *pixel = row + j * 4;
 				pixel[0] = b;
 				pixel[1] = g;
 				pixel[2] = r;
 				pixel[3] = a;
 			} else if (bo->format == DRM_FORMAT_RGBA8888) {
+				uint8_t *pixel = row + j * 4;
 				pixel[0] = r;
 				pixel[1] = g;
 				pixel[2] = b;
 				pixel[3] = a;
+			} else if (bo->format == DRM_FORMAT_NV12) {
+				row[j] = Y;
+				uvrow[(j & ~1u)] = Cb;
+				uvrow[(j & ~1u) + 1] = Cr;
+			} else if (bo->format == DRM_FORMAT_NV21) {
+				row[j] = Y;
+				uvrow[(j & ~1u)] = Cr;
+				uvrow[(j & ~1u) + 1] = Cb;
 			}
 		}
 	}
@@ -60,6 +84,13 @@
 	handles[0] = bo->handle;
 	pitches[0] = bo->pitch;
 	offsets[0] = 0;
+	if (bo->format == DRM_FORMAT_NV12 ||
+	    bo->format == DRM_FORMAT_NV21) {
+		bo->height = (bo->height / 3) * 2;
+		handles[1] = bo->handle;
+		pitches[1] = bo->pitch;
+		offsets[1] = bo->height * bo->pitch;
+	}
 
 	ret = drmModeAddFB2(bo->dev->fd, bo->width, bo->height,
 			format, handles, pitches, offsets,
diff --git a/dev.c b/dev.c
index 508834f..03e29cb 100644
--- a/dev.c
+++ b/dev.c
@@ -85,7 +85,14 @@
 
 	r = drmModeGetResources(dev->fd);
 	if (!r) {
-		printf("failed to get r\n");
+		printf("%s failed to get r\n", devPath);
+		goto err;
+	}
+
+	if (!r->count_crtcs ||
+	    !r->count_encoders ||
+	    !r->count_connectors) {
+		printf("%s no display resources\n", devPath);
 		goto err;
 	}
 
diff --git a/nv12_test.c b/nv12_test.c
new file mode 100644
index 0000000..ef0ab3e
--- /dev/null
+++ b/nv12_test.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "bo.h"
+#include "dev.h"
+
+static int
+is_internal(uint32_t connector_type)
+{
+	return connector_type == DRM_MODE_CONNECTOR_LVDS ||
+	       connector_type == DRM_MODE_CONNECTOR_eDP ||
+	       connector_type == DRM_MODE_CONNECTOR_DSI;
+}
+
+int
+find_crtc_encoder_connector(int fd, drmModeRes *resources, uint32_t *crtc_id,
+			    uint32_t *encoder_id, uint32_t *connector_id)
+{
+	drmModeConnector *connector = NULL;
+	drmModeEncoder *encoder = NULL;
+	int c, e;
+	for (c = 0; c < resources->count_connectors; c++) {
+		printf("Trying connector %d\n", resources->connectors[c]);
+		connector = drmModeGetConnector(fd, resources->connectors[c]);
+		if (!connector)
+			continue;
+		printf("connected %d type %d count_modes %d\n", connector->connection, connector->count_modes, connector->connector_type);
+		if (connector->connection == DRM_MODE_CONNECTED &&
+		    connector->count_modes > 0 &&
+		    is_internal(connector->connector_type)) {
+			break;
+		}
+		drmModeFreeConnector(connector);
+		connector = NULL;
+	}
+	if (!connector)
+		return 0;
+
+	for (e = 0; e < connector->count_encoders; e++) {
+		printf("trying encoder %d\n", connector->encoders[e]);
+		encoder = drmModeGetEncoder(fd, connector->encoders[e]);
+		if (!encoder)
+			continue;
+
+		for (c = 0; c < resources->count_crtcs; c++) {
+			if (encoder->possible_crtcs & (1u << c)) {
+				printf("trying crtc [%d] = %d\n", c, resources->crtcs[c]);
+				*crtc_id = resources->crtcs[c];
+				*encoder_id = connector->encoders[e];
+				*connector_id = connector->connector_id;
+				drmModeFreeConnector(connector);
+				drmModeFreeEncoder(encoder);
+				return 1;
+			}
+		}
+		drmModeFreeEncoder(encoder);
+	}
+	drmModeFreeConnector(connector);
+	return 0;
+}
+
+int
+find_best_mode(int fd, uint32_t connector_id, drmModeModeInfoPtr mode)
+{
+	int m;
+	int found = 0;
+	drmModeConnector *connector;
+
+	connector = drmModeGetConnector(fd, connector_id);
+	if (!connector)
+		return 0;
+
+	for (m = 0; m < connector->count_modes && !found; m++) {
+		if (connector->modes[m].type & DRM_MODE_TYPE_PREFERRED) {
+			*mode = connector->modes[m];
+			found = 1;
+		}
+	}
+
+	if (!found) {
+		*mode = connector->modes[0];
+		found = 1;
+	}
+	drmModeFreeConnector(connector);
+	return found;
+}
+
+static void
+draw_pattern(struct sp_bo *bo)
+{
+	uint32_t stripw = bo->width / 256;
+	uint32_t striph = bo->height / 4;
+	uint32_t x;
+
+	fill_bo(bo, 0, 0, 0, 0);
+	for (x = 0; x < 256; x++) {
+		draw_rect(bo, x * stripw, 0, stripw, striph, 0, x, x, x);
+		draw_rect(bo, x * stripw, striph, stripw, striph, 0, x, 0, 0);
+		draw_rect(bo, x * stripw, striph * 2, stripw, striph, 0, 0, x, 0);
+		draw_rect(bo, x * stripw, striph * 3, stripw, striph, 0, 0, 0, x);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	drmModeRes *resources;
+	struct sp_dev *dev;
+
+	dev = create_sp_dev();
+	if (!dev) {
+		fprintf(stderr, "Creating DRM device failed\n");
+		return 1;
+	}
+
+	resources = drmModeGetResources(dev->fd);
+	if (!resources) {
+		fprintf(stderr, "drmModeGetResources failed: %s\n",
+			strerror(errno));
+		return 1;
+	}
+
+	{
+		int ret;
+		drmModeModeInfo mode;
+		uint32_t crtc_id = 0, encoder_id = 0, connector_id = 0;
+		struct sp_bo *bo;
+
+		if (!find_crtc_encoder_connector(dev->fd, resources, &crtc_id,
+					     &encoder_id, &connector_id)) {
+			fprintf(stderr,
+				"Could not find crtc, connector and encoder\n");
+			return -1;
+		}
+		printf("Using CRTC:%u ENCODER:%u CONNECTOR:%u\n",
+		       crtc_id, encoder_id, connector_id);
+
+		if (!find_best_mode(dev->fd, connector_id, &mode)) {
+			fprintf(stderr, "Could not find mode for CRTC %d\n",
+			        crtc_id);
+			return -1;
+		}
+		printf("Using mode %s\n", mode.name);
+
+		printf("Creating buffer %ux%u\n", mode.hdisplay, mode.vdisplay);
+		bo = create_sp_bo(dev, mode.hdisplay, mode.vdisplay + mode.vdisplay/2, 8, 8,
+				  DRM_FORMAT_NV12, 0);
+
+		draw_pattern(bo);
+
+		ret = drmModeSetCrtc(dev->fd, crtc_id, bo->fb_id, 0, 0,
+				     &connector_id, 1, &mode);
+		if (ret < 0) {
+			fprintf(stderr, "Could not set mode on CRTC %d %s\n",
+				crtc_id, strerror(errno));
+			return 1;
+		}
+
+		sleep(5);
+
+		ret = drmModeSetCrtc(dev->fd, crtc_id, 0, 0, 0, NULL, 0,
+				     NULL);
+		if (ret < 0) {
+			fprintf(stderr, "Could not disable CRTC %d %s\n",
+				crtc_id, strerror(errno));
+		}
+		free_sp_bo(bo);
+	}
+
+	drmModeFreeResources(resources);
+
+
+	return 0;
+}