diff options
author | hnaradla <hanumantha.naradla@linaro.org> | 2014-07-01 14:41:14 +0530 |
---|---|---|
committer | hnaradla <hanumantha.naradla@linaro.org> | 2014-07-01 14:49:47 +0530 |
commit | 47f9008b2d9905c65b4cdcd0fe08d0566f574fbc (patch) | |
tree | eb358ffe5da72229f11201285a6b9aa3b16533f9 | |
parent | 1b0b934ad1ae964697f52d7bfcbbb40946646f68 (diff) |
vidctest: add msmmodetest test app.
1. added msmmodetest app that renders via mdp from ion memory.
2. moved docoder test sources to 'decodertest'.
-rwxr-xr-x[-rw-r--r--] | decodertest/Makefile (renamed from Makefile) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/README (renamed from README) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/decoder_driver_test.c (renamed from decoder_driver_test.c) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/decoder_driver_test.h (renamed from decoder_driver_test.h) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/ion.h (renamed from ion.h) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/message_queue.c (renamed from message_queue.c) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/message_queue.h (renamed from message_queue.h) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/msm_ion.h (renamed from msm_ion.h) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/msm_vidc_dec.h (renamed from msm_vidc_dec.h) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/queue.c (renamed from queue.c) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | decodertest/queue.h (renamed from queue.h) | 0 | ||||
-rwxr-xr-x | modetest/Makefile | 22 | ||||
-rwxr-xr-x | modetest/buffers.c | 1231 | ||||
-rwxr-xr-x | modetest/buffers.h | 46 | ||||
-rwxr-xr-x | modetest/ion.h | 868 | ||||
-rwxr-xr-x | modetest/libmsm.c | 138 | ||||
-rwxr-xr-x | modetest/libmsm.h | 82 | ||||
-rwxr-xr-x | modetest/msmdumb.c | 217 | ||||
-rwxr-xr-x | modetest/msmdumb_priv.h | 68 | ||||
-rwxr-xr-x | modetest/msmmodetest.c | 1564 |
20 files changed, 4236 insertions, 0 deletions
diff --git a/Makefile b/decodertest/Makefile index d6d4122..d6d4122 100644..100755 --- a/Makefile +++ b/decodertest/Makefile diff --git a/README b/decodertest/README index 534fb81..534fb81 100644..100755 --- a/README +++ b/decodertest/README diff --git a/decoder_driver_test.c b/decodertest/decoder_driver_test.c index 935a9ab..935a9ab 100644..100755 --- a/decoder_driver_test.c +++ b/decodertest/decoder_driver_test.c diff --git a/decoder_driver_test.h b/decodertest/decoder_driver_test.h index cbdddaf..cbdddaf 100644..100755 --- a/decoder_driver_test.h +++ b/decodertest/decoder_driver_test.h diff --git a/ion.h b/decodertest/ion.h index f40abd0..f40abd0 100644..100755 --- a/ion.h +++ b/decodertest/ion.h diff --git a/message_queue.c b/decodertest/message_queue.c index 8cfa9fe..8cfa9fe 100644..100755 --- a/message_queue.c +++ b/decodertest/message_queue.c diff --git a/message_queue.h b/decodertest/message_queue.h index 7e15724..7e15724 100644..100755 --- a/message_queue.h +++ b/decodertest/message_queue.h diff --git a/msm_ion.h b/decodertest/msm_ion.h index 4292fa3..4292fa3 100644..100755 --- a/msm_ion.h +++ b/decodertest/msm_ion.h diff --git a/msm_vidc_dec.h b/decodertest/msm_vidc_dec.h index 7092ccb..7092ccb 100644..100755 --- a/msm_vidc_dec.h +++ b/decodertest/msm_vidc_dec.h diff --git a/queue.c b/decodertest/queue.c index e0aeb9a..e0aeb9a 100644..100755 --- a/queue.c +++ b/decodertest/queue.c diff --git a/queue.h b/decodertest/queue.h index 195d9b2..195d9b2 100644..100755 --- a/queue.h +++ b/decodertest/queue.h diff --git a/modetest/Makefile b/modetest/Makefile new file mode 100755 index 0000000..a7c3112 --- /dev/null +++ b/modetest/Makefile @@ -0,0 +1,22 @@ +all: msmmodetest + +IDIR =/usr/include/libdrm/ +CFLAGS=-I$(IDIR) + +LFLAGS = -L/usr/local/lib -L/usr/lib/arm-linux-gnueabihf + +LIBS = -ldrm + +SRC=msmdumb.c libmsm.c buffers.c msmmodetest.c buffers.h +OBJ=$(patsubst %.c,%.o,$(SRC)) +CC = $(CROSS_COMPILE)gcc + +msmmodetest: $(OBJ) + $(CC) -pthread $^ -o $@ $(CFLAGS) $(LFLAGS) $(LIBS) + +%.o: %.c + $(CC) -c -o $@ $< $(CFLAGS) + +clean: + -rm -f $(OBJ) + -rm -f msmmodetest diff --git a/modetest/buffers.c b/modetest/buffers.c new file mode 100755 index 0000000..25dc847 --- /dev/null +++ b/modetest/buffers.c @@ -0,0 +1,1231 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz <jakob@tungstengraphics.com> + * Copyright 2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +//#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "drm_fourcc.h" +#include "libmsm.h" + +#include "buffers.h" + +#ifdef HAVE_CAIRO +#include <math.h> +#include <cairo.h> +#endif + +#include <sys/ioctl.h> +#include <fcntl.h> +#include "ion.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* ----------------------------------------------------------------------------- + * Formats + */ + +struct color_component { + unsigned int length; + unsigned int offset; +}; + +struct rgb_info { + struct color_component red; + struct color_component green; + struct color_component blue; + struct color_component alpha; +}; + +enum yuv_order { + YUV_YCbCr = 1, + YUV_YCrCb = 2, + YUV_YC = 4, + YUV_CY = 8, +}; + +struct yuv_info { + enum yuv_order order; + unsigned int xsub; + unsigned int ysub; + unsigned int chroma_stride; +}; + +struct format_info { + unsigned int format; + const char *name; + const struct rgb_info rgb; + const struct yuv_info yuv; +}; + +#define MAKE_RGB_INFO(rl, ro, bl, bo, gl, go, al, ao) \ + .rgb = { { (rl), (ro) }, { (bl), (bo) }, { (gl), (go) }, { (al), (ao) } } + +#define MAKE_YUV_INFO(order, xsub, ysub, chroma_stride) \ + .yuv = { (order), (xsub), (ysub), (chroma_stride) } + +static const struct format_info format_info[] = { + /* YUV packed */ + { DRM_FORMAT_UYVY, "UYVY", MAKE_YUV_INFO(YUV_YCbCr | YUV_CY, 2, 2, 2) }, + { DRM_FORMAT_VYUY, "VYUY", MAKE_YUV_INFO(YUV_YCrCb | YUV_CY, 2, 2, 2) }, + { DRM_FORMAT_YUYV, "YUYV", MAKE_YUV_INFO(YUV_YCbCr | YUV_YC, 2, 2, 2) }, + { DRM_FORMAT_YVYU, "YVYU", MAKE_YUV_INFO(YUV_YCrCb | YUV_YC, 2, 2, 2) }, + /* YUV semi-planar */ + { DRM_FORMAT_NV12, "NV12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 2) }, + { DRM_FORMAT_NV21, "NV21", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 2) }, + { DRM_FORMAT_NV16, "NV16", MAKE_YUV_INFO(YUV_YCbCr, 2, 1, 2) }, + { DRM_FORMAT_NV61, "NV61", MAKE_YUV_INFO(YUV_YCrCb, 2, 1, 2) }, + /* YUV planar */ + { DRM_FORMAT_YUV420, "YU12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 1) }, + { DRM_FORMAT_YVU420, "YV12", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 1) }, + /* RGB16 */ + { DRM_FORMAT_ARGB4444, "AR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 4, 12) }, + { DRM_FORMAT_XRGB4444, "XR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 0, 0) }, + { DRM_FORMAT_ABGR4444, "AB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 4, 12) }, + { DRM_FORMAT_XBGR4444, "XB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 0, 0) }, + { DRM_FORMAT_RGBA4444, "RA12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 4, 0) }, + { DRM_FORMAT_RGBX4444, "RX12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 0, 0) }, + { DRM_FORMAT_BGRA4444, "BA12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 4, 0) }, + { DRM_FORMAT_BGRX4444, "BX12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 0, 0) }, + { DRM_FORMAT_ARGB1555, "AR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 1, 15) }, + { DRM_FORMAT_XRGB1555, "XR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 0, 0) }, + { DRM_FORMAT_ABGR1555, "AB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 1, 15) }, + { DRM_FORMAT_XBGR1555, "XB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 0, 0) }, + { DRM_FORMAT_RGBA5551, "RA15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 1, 0) }, + { DRM_FORMAT_RGBX5551, "RX15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 0, 0) }, + { DRM_FORMAT_BGRA5551, "BA15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 1, 0) }, + { DRM_FORMAT_BGRX5551, "BX15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 0, 0) }, + { DRM_FORMAT_RGB565, "RG16", MAKE_RGB_INFO(5, 11, 6, 5, 5, 0, 0, 0) }, + { DRM_FORMAT_BGR565, "BG16", MAKE_RGB_INFO(5, 0, 6, 5, 5, 11, 0, 0) }, + /* RGB24 */ + { DRM_FORMAT_BGR888, "BG24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) }, + { DRM_FORMAT_RGB888, "RG24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) }, + /* RGB32 */ + { DRM_FORMAT_ARGB8888, "AR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 8, 24) }, + { DRM_FORMAT_XRGB8888, "XR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) }, + { DRM_FORMAT_ABGR8888, "AB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 8, 24) }, + { DRM_FORMAT_XBGR8888, "XB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) }, + { DRM_FORMAT_RGBA8888, "RA24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 8, 0) }, + { DRM_FORMAT_RGBX8888, "RX24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 0, 0) }, + { DRM_FORMAT_BGRA8888, "BA24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 8, 0) }, + { DRM_FORMAT_BGRX8888, "BX24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 0, 0) }, + { DRM_FORMAT_ARGB2101010, "AR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 2, 30) }, + { DRM_FORMAT_XRGB2101010, "XR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 0, 0) }, + { DRM_FORMAT_ABGR2101010, "AB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 2, 30) }, + { DRM_FORMAT_XBGR2101010, "XB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 0, 0) }, + { DRM_FORMAT_RGBA1010102, "RA30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 2, 0) }, + { DRM_FORMAT_RGBX1010102, "RX30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 0, 0) }, + { DRM_FORMAT_BGRA1010102, "BA30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 2, 0) }, + { DRM_FORMAT_BGRX1010102, "BX30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 0, 0) }, +}; + +unsigned int format_fourcc(const char *name) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(format_info); i++) { + if (!strcmp(format_info[i].name, name)) + return format_info[i].format; + } + return 0; +} + +/* ----------------------------------------------------------------------------- + * Test patterns + */ + +struct color_rgb24 { + unsigned int value:24; +} __attribute__((__packed__)); + +struct color_yuv { + unsigned char y; + unsigned char u; + unsigned char v; +}; + +#define MAKE_YUV_601_Y(r, g, b) \ + ((( 66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16) +#define MAKE_YUV_601_U(r, g, b) \ + (((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128) +#define MAKE_YUV_601_V(r, g, b) \ + (((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128) + +#define MAKE_YUV_601(r, g, b) \ + { .y = MAKE_YUV_601_Y(r, g, b), \ + .u = MAKE_YUV_601_U(r, g, b), \ + .v = MAKE_YUV_601_V(r, g, b) } + +#define MAKE_RGBA(rgb, r, g, b, a) \ + ((((r) >> (8 - (rgb)->red.length)) << (rgb)->red.offset) | \ + (((g) >> (8 - (rgb)->green.length)) << (rgb)->green.offset) | \ + (((b) >> (8 - (rgb)->blue.length)) << (rgb)->blue.offset) | \ + (((a) >> (8 - (rgb)->alpha.length)) << (rgb)->alpha.offset)) + +#define MAKE_RGB24(rgb, r, g, b) \ + { .value = MAKE_RGBA(rgb, r, g, b, 0) } + +static void +fill_smpte_yuv_planar(const struct yuv_info *yuv, + unsigned char *y_mem, unsigned char *u_mem, + unsigned char *v_mem, unsigned int width, + unsigned int height, unsigned int stride) +{ + const struct color_yuv colors_top[] = { + MAKE_YUV_601(191, 192, 192), /* grey */ + MAKE_YUV_601(192, 192, 0), /* yellow */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(0, 192, 0), /* green */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(192, 0, 0), /* red */ + MAKE_YUV_601(0, 0, 192), /* blue */ + }; + const struct color_yuv colors_middle[] = { + MAKE_YUV_601(0, 0, 192), /* blue */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 192, 192), /* grey */ + }; + const struct color_yuv colors_bottom[] = { + MAKE_YUV_601(0, 33, 76), /* in-phase */ + MAKE_YUV_601(255, 255, 255), /* super white */ + MAKE_YUV_601(50, 0, 106), /* quadrature */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(9, 9, 9), /* 3.5% */ + MAKE_YUV_601(19, 19, 19), /* 7.5% */ + MAKE_YUV_601(29, 29, 29), /* 11.5% */ + MAKE_YUV_601(19, 19, 19), /* black */ + }; + unsigned int cs = yuv->chroma_stride; + unsigned int xsub = yuv->xsub; + unsigned int ysub = yuv->ysub; + unsigned int x; + unsigned int y; + + /* Luma */ + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[x] = colors_top[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[x] = colors_middle[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + y_mem[x] = colors_bottom[x * 4 / (width * 5 / 7)].y; + for (; x < width * 6 / 7; ++x) + y_mem[x] = colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4].y; + for (; x < width; ++x) + y_mem[x] = colors_bottom[7].y; + y_mem += stride; + } + + /* Chroma */ + for (y = 0; y < height / ysub * 6 / 9; ++y) { + for (x = 0; x < width; x += xsub) { + u_mem[x*cs/xsub] = colors_top[x * 7 / width].u; + v_mem[x*cs/xsub] = colors_top[x * 7 / width].v; + } + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } + + for (; y < height / ysub * 7 / 9; ++y) { + for (x = 0; x < width; x += xsub) { + u_mem[x*cs/xsub] = colors_middle[x * 7 / width].u; + v_mem[x*cs/xsub] = colors_middle[x * 7 / width].v; + } + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } + + for (; y < height / ysub; ++y) { + for (x = 0; x < width * 5 / 7; x += xsub) { + u_mem[x*cs/xsub] = + colors_bottom[x * 4 / (width * 5 / 7)].u; + v_mem[x*cs/xsub] = + colors_bottom[x * 4 / (width * 5 / 7)].v; + } + for (; x < width * 6 / 7; x += xsub) { + u_mem[x*cs/xsub] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].u; + v_mem[x*cs/xsub] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].v; + } + for (; x < width; x += xsub) { + u_mem[x*cs/xsub] = colors_bottom[7].u; + v_mem[x*cs/xsub] = colors_bottom[7].v; + } + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } +} + +static void +fill_smpte_yuv_packed(const struct yuv_info *yuv, unsigned char *mem, + unsigned int width, unsigned int height, + unsigned int stride) +{ + const struct color_yuv colors_top[] = { + MAKE_YUV_601(191, 192, 192), /* grey */ + MAKE_YUV_601(192, 192, 0), /* yellow */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(0, 192, 0), /* green */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(192, 0, 0), /* red */ + MAKE_YUV_601(0, 0, 192), /* blue */ + }; + const struct color_yuv colors_middle[] = { + MAKE_YUV_601(0, 0, 192), /* blue */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 192, 192), /* grey */ + }; + const struct color_yuv colors_bottom[] = { + MAKE_YUV_601(0, 33, 76), /* in-phase */ + MAKE_YUV_601(255, 255, 255), /* super white */ + MAKE_YUV_601(50, 0, 106), /* quadrature */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(9, 9, 9), /* 3.5% */ + MAKE_YUV_601(19, 19, 19), /* 7.5% */ + MAKE_YUV_601(29, 29, 29), /* 11.5% */ + MAKE_YUV_601(19, 19, 19), /* black */ + }; + unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1; + unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1; + unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0; + unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0; + unsigned int x; + unsigned int y; + + /* Luma */ + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[2*x] = colors_top[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[2*x] = colors_middle[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + y_mem[2*x] = colors_bottom[x * 4 / (width * 5 / 7)].y; + for (; x < width * 6 / 7; ++x) + y_mem[2*x] = colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4].y; + for (; x < width; ++x) + y_mem[2*x] = colors_bottom[7].y; + y_mem += stride; + } + + /* Chroma */ + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; x += 2) { + c_mem[2*x+u] = colors_top[x * 7 / width].u; + c_mem[2*x+v] = colors_top[x * 7 / width].v; + } + c_mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; x += 2) { + c_mem[2*x+u] = colors_middle[x * 7 / width].u; + c_mem[2*x+v] = colors_middle[x * 7 / width].v; + } + c_mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; x += 2) { + c_mem[2*x+u] = colors_bottom[x * 4 / (width * 5 / 7)].u; + c_mem[2*x+v] = colors_bottom[x * 4 / (width * 5 / 7)].v; + } + for (; x < width * 6 / 7; x += 2) { + c_mem[2*x+u] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].u; + c_mem[2*x+v] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].v; + } + for (; x < width; x += 2) { + c_mem[2*x+u] = colors_bottom[7].u; + c_mem[2*x+v] = colors_bottom[7].v; + } + c_mem += stride; + } +} + +static void +fill_smpte_rgb16(const struct rgb_info *rgb, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const uint16_t colors_top[] = { + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + MAKE_RGBA(rgb, 192, 192, 0, 255), /* yellow */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 0, 192, 0, 255), /* green */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 192, 0, 0, 255), /* red */ + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + }; + const uint16_t colors_middle[] = { + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + }; + const uint16_t colors_bottom[] = { + MAKE_RGBA(rgb, 0, 33, 76, 255), /* in-phase */ + MAKE_RGBA(rgb, 255, 255, 255, 255), /* super white */ + MAKE_RGBA(rgb, 50, 0, 106, 255), /* quadrature */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 9, 9, 9, 255), /* 3.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* 7.5% */ + MAKE_RGBA(rgb, 29, 29, 29, 255), /* 11.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + }; + unsigned int x; + unsigned int y; + + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint16_t *)mem)[x] = colors_top[x * 7 / width]; + mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint16_t *)mem)[x] = colors_middle[x * 7 / width]; + mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + ((uint16_t *)mem)[x] = + colors_bottom[x * 4 / (width * 5 / 7)]; + for (; x < width * 6 / 7; ++x) + ((uint16_t *)mem)[x] = + colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4]; + for (; x < width; ++x) + ((uint16_t *)mem)[x] = colors_bottom[7]; + mem += stride; + } +} + +static void +fill_smpte_rgb24(const struct rgb_info *rgb, void *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct color_rgb24 colors_top[] = { + MAKE_RGB24(rgb, 192, 192, 192), /* grey */ + MAKE_RGB24(rgb, 192, 192, 0), /* yellow */ + MAKE_RGB24(rgb, 0, 192, 192), /* cyan */ + MAKE_RGB24(rgb, 0, 192, 0), /* green */ + MAKE_RGB24(rgb, 192, 0, 192), /* magenta */ + MAKE_RGB24(rgb, 192, 0, 0), /* red */ + MAKE_RGB24(rgb, 0, 0, 192), /* blue */ + }; + const struct color_rgb24 colors_middle[] = { + MAKE_RGB24(rgb, 0, 0, 192), /* blue */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 192, 0, 192), /* magenta */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 0, 192, 192), /* cyan */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 192, 192, 192), /* grey */ + }; + const struct color_rgb24 colors_bottom[] = { + MAKE_RGB24(rgb, 0, 33, 76), /* in-phase */ + MAKE_RGB24(rgb, 255, 255, 255), /* super white */ + MAKE_RGB24(rgb, 50, 0, 106), /* quadrature */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 9, 9, 9), /* 3.5% */ + MAKE_RGB24(rgb, 19, 19, 19), /* 7.5% */ + MAKE_RGB24(rgb, 29, 29, 29), /* 11.5% */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + }; + unsigned int x; + unsigned int y; + + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_top[x * 7 / width]; + mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_middle[x * 7 / width]; + mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_bottom[x * 4 / (width * 5 / 7)]; + for (; x < width * 6 / 7; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4]; + for (; x < width; ++x) + ((struct color_rgb24 *)mem)[x] = colors_bottom[7]; + mem += stride; + } +} + +static void +fill_smpte_rgb32(const struct rgb_info *rgb, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const uint32_t colors_top[] = { + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + MAKE_RGBA(rgb, 192, 192, 0, 255), /* yellow */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 0, 192, 0, 255), /* green */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 192, 0, 0, 255), /* red */ + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + }; + const uint32_t colors_middle[] = { + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + }; + const uint32_t colors_bottom[] = { + MAKE_RGBA(rgb, 0, 33, 76, 255), /* in-phase */ + MAKE_RGBA(rgb, 255, 255, 255, 255), /* super white */ + MAKE_RGBA(rgb, 50, 0, 106, 255), /* quadrature */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 9, 9, 9, 255), /* 3.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* 7.5% */ + MAKE_RGBA(rgb, 29, 29, 29, 255), /* 11.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + }; + unsigned int x; + unsigned int y; + + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint32_t *)mem)[x] = colors_top[x * 7 / width]; + mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint32_t *)mem)[x] = colors_middle[x * 7 / width]; + mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + ((uint32_t *)mem)[x] = + colors_bottom[x * 4 / (width * 5 / 7)]; + for (; x < width * 6 / 7; ++x) + ((uint32_t *)mem)[x] = + colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4]; + for (; x < width; ++x) + ((uint32_t *)mem)[x] = colors_bottom[7]; + mem += stride; + } +} + +static void +fill_smpte(const struct format_info *info, void *planes[3], unsigned int width, + unsigned int height, unsigned int stride) +{ + unsigned char *u, *v; + + switch (info->format) { + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + return fill_smpte_yuv_packed(&info->yuv, planes[0], width, + height, stride); + + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1; + v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1; + return fill_smpte_yuv_planar(&info->yuv, planes[0], u, v, + width, height, stride); + + case DRM_FORMAT_YUV420: + return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[1], + planes[2], width, height, stride); + + case DRM_FORMAT_YVU420: + return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[2], + planes[1], width, height, stride); + + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX5551: + return fill_smpte_rgb16(&info->rgb, planes[0], + width, height, stride); + + case DRM_FORMAT_BGR888: + case DRM_FORMAT_RGB888: + return fill_smpte_rgb24(&info->rgb, planes[0], + width, height, stride); + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRX1010102: + return fill_smpte_rgb32(&info->rgb, planes[0], + width, height, stride); + } +} + +/* swap these for big endian.. */ +#define RED 2 +#define GREEN 1 +#define BLUE 0 + +static void +make_pwetty(void *data, int width, int height, int stride, uint32_t format) +{ +#ifdef HAVE_CAIRO + cairo_surface_t *surface; + cairo_t *cr; + int x, y; + cairo_format_t cairo_format; + + /* we can ignore the order of R,G,B channels */ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + cairo_format = CAIRO_FORMAT_ARGB32; + break; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + cairo_format = CAIRO_FORMAT_RGB16_565; + break; + default: + return; + } + + surface = cairo_image_surface_create_for_data(data, + cairo_format, + width, height, + stride); + cr = cairo_create(surface); + cairo_surface_destroy(surface); + + cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE); + for (x = 0; x < width; x += 250) + for (y = 0; y < height; y += 250) { + char buf[64]; + + cairo_move_to(cr, x, y - 20); + cairo_line_to(cr, x, y + 20); + cairo_move_to(cr, x - 20, y); + cairo_line_to(cr, x + 20, y); + cairo_new_sub_path(cr); + cairo_arc(cr, x, y, 10, 0, M_PI * 2); + cairo_set_line_width(cr, 4); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_set_line_width(cr, 2); + cairo_stroke(cr); + + snprintf(buf, sizeof buf, "%d, %d", x, y); + cairo_move_to(cr, x + 20, y + 20); + cairo_text_path(cr, buf); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_fill(cr); + } + + cairo_destroy(cr); +#endif +} + +static void +fill_tiles_yuv_planar(const struct format_info *info, + unsigned char *y_mem, unsigned char *u_mem, + unsigned char *v_mem, unsigned int width, + unsigned int height, unsigned int stride) +{ + const struct yuv_info *yuv = &info->yuv; + unsigned int cs = yuv->chroma_stride; + unsigned int xsub = yuv->xsub; + unsigned int ysub = yuv->ysub; + unsigned int x; + unsigned int y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x+y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + struct color_yuv color = + MAKE_YUV_601((rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff); + + y_mem[x] = color.y; + u_mem[x/xsub*cs] = color.u; + v_mem[x/xsub*cs] = color.v; + } + + y_mem += stride; + if ((y + 1) % ysub == 0) { + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } + } +} + +static void +fill_tiles_yuv_packed(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, + unsigned int stride) +{ + const struct yuv_info *yuv = &info->yuv; + unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1; + unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1; + unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0; + unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0; + unsigned int x; + unsigned int y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; x += 2) { + div_t d = div(x+y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + struct color_yuv color = + MAKE_YUV_601((rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff); + + y_mem[2*x] = color.y; + c_mem[2*x+u] = color.u; + y_mem[2*x+2] = color.y; + c_mem[2*x+v] = color.v; + } + + y_mem += stride; + c_mem += stride; + } +} + +static void +fill_tiles_rgb16(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct rgb_info *rgb = &info->rgb; + unsigned char *mem_base = mem; + unsigned int x, y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x+y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + uint16_t color = + MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff, + 255); + + ((uint16_t *)mem)[x] = color; + } + mem += stride; + } + + make_pwetty(mem_base, width, height, stride, info->format); +} + +static void +fill_tiles_rgb24(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct rgb_info *rgb = &info->rgb; + unsigned int x, y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x+y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + struct color_rgb24 color = + MAKE_RGB24(rgb, (rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff); + + ((struct color_rgb24 *)mem)[x] = color; + } + mem += stride; + } +} + +static void +fill_tiles_rgb32(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct rgb_info *rgb = &info->rgb; + unsigned char *mem_base = mem; + unsigned int x, y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x+y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + uint32_t color = + MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff, + 255); + + ((uint32_t *)mem)[x] = color; + } + mem += stride; + } + + make_pwetty(mem_base, width, height, stride, info->format); +} + +static void +fill_tiles(const struct format_info *info, void *planes[3], unsigned int width, + unsigned int height, unsigned int stride) +{ + unsigned char *u, *v; + + switch (info->format) { + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + return fill_tiles_yuv_packed(info, planes[0], + width, height, stride); + + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1; + v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1; + return fill_tiles_yuv_planar(info, planes[0], u, v, + width, height, stride); + + case DRM_FORMAT_YUV420: + return fill_tiles_yuv_planar(info, planes[0], planes[1], + planes[2], width, height, stride); + + case DRM_FORMAT_YVU420: + return fill_tiles_yuv_planar(info, planes[0], planes[2], + planes[1], width, height, stride); + + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX5551: + return fill_tiles_rgb16(info, planes[0], + width, height, stride); + + case DRM_FORMAT_BGR888: + case DRM_FORMAT_RGB888: + return fill_tiles_rgb24(info, planes[0], + width, height, stride); + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRX1010102: + return fill_tiles_rgb32(info, planes[0], + width, height, stride); + } +} + +static void +fill_plain(const struct format_info *info, void *planes[3], unsigned int width, + unsigned int height, unsigned int stride) +{ + memset(planes[0], 0x77, stride * height); +} + +/* + * fill_pattern - Fill a buffer with a test pattern + * @format: Pixel format + * @pattern: Test pattern + * @buffer: Buffer memory + * @width: Width in pixels + * @height: Height in pixels + * @stride: Line stride (pitch) in bytes + * + * Fill the buffer with the test pattern specified by the pattern parameter. + * Supported formats vary depending on the selected pattern. + */ +static void +fill_pattern(unsigned int format, enum fill_pattern pattern, void *planes[3], + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct format_info *info = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_info); ++i) { + if (format_info[i].format == format) { + info = &format_info[i]; + break; + } + } + + if (info == NULL) + return; + + switch (pattern) { + case PATTERN_TILES: + return fill_tiles(info, planes, width, height, stride); + + case PATTERN_SMPTE: + return fill_smpte(info, planes, width, height, stride); + + case PATTERN_PLAIN: + return fill_plain(info, planes, width, height, stride); + + default: + printf("Error: unsupported test pattern %u.\n", pattern); + break; + } +} + +/* + * ion_allocate_buffer - Allocate Ion Buffer + * @width: Width in pixels + * @height: Height in pixels + * + * Returns the file descriptor that can be passed around for sharing + */ +static int +ion_allocate_buffer(unsigned int width, unsigned int height) +{ + int ion_devicefd = 0; + struct ion_fd_data fd_data; + struct ion_allocation_data alloc_data; + int rc = -1; + int pitch = 0; + int size = 0; + int bpp = 32; // Inline with BO_TYPE s that are 32bpp formats + + /* Open ion drive for memory allocations */ + ion_devicefd = open ("/dev/ion", O_RDONLY); + + if (ion_devicefd < 0) { + fprintf(stderr, "ERROR: ION Device open() Failed --------\n"); + return -1; + } +#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1)) + pitch = ((bpp + 7)/8) * (ALIGN(width, 32)); + size = ALIGN(pitch * height, 0x1000); + + alloc_data.len = size; + alloc_data.flags = ION_HEAP(ION_IOMMU_HEAP_ID); + alloc_data.align = 0x1000; // 2048; + //alloc_data.heap_mask = 0x2000000; + + rc = ioctl(ion_devicefd, ION_IOC_ALLOC, &alloc_data); + if (rc || !alloc_data.handle) { + fprintf(stderr, "ION ALLOC failed, ion_devicefd = %d, rc = %d\n" + "error = %s, handle = 0x%p", ion_devicefd, + rc, strerror(errno), alloc_data.handle); + alloc_data.handle = NULL; + return -1; + } + + fd_data.handle = alloc_data.handle; + + rc = ioctl(ion_devicefd,ION_IOC_SHARE,&fd_data); + if (rc) { + fprintf(stderr, "ON_IOC_SHARE failed, fd = %d, handle = 0x%p\n", + ion_devicefd, fd_data.handle); + fd_data.fd = -1; + return -1; + } + fprintf(stderr, "ION_IOC_SHARE success, fd = %d\n", fd_data.fd); + return fd_data.fd; +} + +/* ----------------------------------------------------------------------------- + * Buffers management + */ + +static struct msm_bo * +allocate_buffer(struct msm_driver *msm, unsigned int width, unsigned int height, + unsigned int *stride) +{ + struct msm_bo *bo; + unsigned bo_attribs[] = { + MSM_WIDTH, 0, + MSM_HEIGHT, 0, + MSM_BO_TYPE, MSM_BO_TYPE_SCANOUT_X8R8G8B8, + MSM_TERMINATE_PROP_LIST + }; + int ret; + int buf_fd = 0; + bo_attribs[1] = width; + bo_attribs[3] = height; + + buf_fd = ion_allocate_buffer(width, height); + ret = msm_bo_create(msm, bo_attribs, &bo, buf_fd); + if (ret) { + fprintf(stderr, "failed to alloc buffer: %s\n", + strerror(-ret)); + return NULL; + } + + ret = msm_bo_get_prop(bo, MSM_PITCH, stride); + if (ret) { + fprintf(stderr, "failed to retreive buffer stride: %s\n", + strerror(-ret)); + msm_bo_destroy(&bo); + return NULL; + } + + return bo; +} + +struct msm_bo * +create_test_buffer(struct msm_driver *msm, unsigned int format, + unsigned int width, unsigned int height, + unsigned int handles[4], unsigned int pitches[4], + unsigned int offsets[4], enum fill_pattern pattern) +{ + unsigned int virtual_height; + struct msm_bo *bo; + void *planes[3] = { 0, }; + void *virtual; + int ret; + + switch (format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + virtual_height = height * 3 / 2; + break; + + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + virtual_height = height * 2; + break; + + default: + virtual_height = height; + break; + } + + bo = allocate_buffer(msm, width, virtual_height, &pitches[0]); + if (!bo) + return NULL; + + ret = msm_bo_map(bo, &virtual); + if (ret) { + fprintf(stderr, "failed to map buffer: %s\n", + strerror(-ret)); + msm_bo_destroy(&bo); + return NULL; + } + fprintf(stderr, "msm_bo_map virtual pointer: %p \n", virtual); + /* just testing a limited # of formats to test single + * and multi-planar path.. would be nice to add more.. + */ + switch (format) { + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + offsets[0] = 0; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[0]); + msm_bo_get_prop(bo, MSM_PITCH, &pitches[0]); + + planes[0] = virtual; + break; + + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + offsets[0] = 0; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[0]); + msm_bo_get_prop(bo, MSM_PITCH, &pitches[0]); + pitches[1] = pitches[0]; + offsets[1] = pitches[0] * height; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[1]); + + planes[0] = virtual; + planes[1] = virtual + offsets[1]; + break; + + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + offsets[0] = 0; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[0]); + msm_bo_get_prop(bo, MSM_PITCH, &pitches[0]); + pitches[1] = pitches[0] / 2; + offsets[1] = pitches[0] * height; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[1]); + pitches[2] = pitches[1]; + offsets[2] = offsets[1] + pitches[1] * height / 2; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[2]); + + planes[0] = virtual; + planes[1] = virtual + offsets[1]; + planes[2] = virtual + offsets[2]; + break; + + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRX1010102: + offsets[0] = 0; + msm_bo_get_prop(bo, MSM_HANDLE, &handles[0]); + msm_bo_get_prop(bo, MSM_PITCH, &pitches[0]); + + planes[0] = virtual; + break; + } + + fill_pattern(format, pattern, planes, width, height, pitches[0]); + msm_bo_unmap(bo); + + return bo; +} diff --git a/modetest/buffers.h b/modetest/buffers.h new file mode 100755 index 0000000..81c6da6 --- /dev/null +++ b/modetest/buffers.h @@ -0,0 +1,46 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz <jakob@tungstengraphics.com> + * Copyright 2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __BUFFERS_H__ +#define __BUFFERS_H__ + +struct msm_bo; +struct msm_driver; + +enum fill_pattern { + PATTERN_TILES = 0, + PATTERN_PLAIN = 1, + PATTERN_SMPTE = 2, +}; + +struct msm_bo *create_test_buffer(struct msm_driver *msm, unsigned int format, + unsigned int width, unsigned int height, + unsigned int handles[4], unsigned int pitches[4], + unsigned int offsets[4], enum fill_pattern pattern); + +unsigned int format_fourcc(const char *name); + +#endif diff --git a/modetest/ion.h b/modetest/ion.h new file mode 100755 index 0000000..2519270 --- /dev/null +++ b/modetest/ion.h @@ -0,0 +1,868 @@ +/* + * include/linux/ion.h + * + * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_ION_H +#define _LINUX_ION_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +struct ion_handle; +/** + * enum ion_heap_types - list of all possible types of heaps + * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc + * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc + * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous + * @ION_HEAP_TYPE_IOMMU: IOMMU memory + * @ION_HEAP_TYPE_CP: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous. Used for content protection. + * @ION_HEAP_END: helper for iterating over heaps + */ +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_IOMMU, + ION_HEAP_TYPE_CP, + ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always + are at the end of this enum */ + ION_NUM_HEAPS, +}; + +#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) +#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) +#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) +#define ION_HEAP_CP_MASK (1 << ION_HEAP_TYPE_CP) + + +/** + * These are the only ids that should be used for Ion heap ids. + * The ids listed are the order in which allocation will be attempted + * if specified. Don't swap the order of heap ids unless you know what + * you are doing! + * Id's are spaced by purpose to allow new Id's to be inserted in-between (for + * possible fallbacks) + */ + +enum ion_heap_ids { + INVALID_HEAP_ID = -1, + ION_CP_MM_HEAP_ID = 8, + ION_CP_MFC_HEAP_ID = 12, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SF_HEAP_ID = 24, + ION_IOMMU_HEAP_ID = 25, + ION_QSECOM_HEAP_ID = 27, + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_SYSTEM_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_SECURE flag */ +}; + +enum ion_fixed_position { + NOT_FIXED, + FIXED_LOW, + FIXED_MIDDLE, + FIXED_HIGH, +}; + +enum cp_mem_usage { + VIDEO_BITSTREAM = 0x1, + VIDEO_PIXEL = 0x2, + VIDEO_NONPIXEL = 0x3, + MAX_USAGE = 0x4, + UNKNOWN = 0x7FFFFFFF, +}; + +/** + * Flag to use when allocating to indicate that a heap is secure. + */ +#define ION_SECURE (1 << ION_HEAP_ID_RESERVED) + +/** + * Macro should be used with ion_heap_ids defined above. + */ +#define ION_HEAP(bit) (1 << (bit)) + +#define ION_VMALLOC_HEAP_NAME "vmalloc" +#define ION_AUDIO_HEAP_NAME "audio" +#define ION_SF_HEAP_NAME "sf" +#define ION_MM_HEAP_NAME "mm" +#define ION_CAMERA_HEAP_NAME "camera_preview" +#define ION_IOMMU_HEAP_NAME "iommu" +#define ION_MFC_HEAP_NAME "mfc" +#define ION_WB_HEAP_NAME "wb" +#define ION_MM_FIRMWARE_HEAP_NAME "mm_fw" +#define ION_QSECOM_HEAP_NAME "qsecom" +#define ION_FMEM_HEAP_NAME "fmem" + +#define CACHED 1 +#define UNCACHED 0 + +#define ION_CACHE_SHIFT 0 + +#define ION_SET_CACHE(__cache) ((__cache) << ION_CACHE_SHIFT) + +#define ION_IS_CACHED(__flags) ((__flags) & (1 << ION_CACHE_SHIFT)) + +/* + * This flag allows clients when mapping into the IOMMU to specify to + * defer un-mapping from the IOMMU until the buffer memory is freed. + */ +#define ION_IOMMU_UNMAP_DELAYED 1 + +#ifdef __KERNEL__ +#include <linux/err.h> +#include <mach/ion.h> +struct ion_device; +struct ion_heap; +struct ion_mapper; +struct ion_client; +struct ion_buffer; + +/* This should be removed some day when phys_addr_t's are fully + plumbed in the kernel, and all instances of ion_phys_addr_t should + be converted to phys_addr_t. For the time being many kernel interfaces + do not accept phys_addr_t's that would have to */ +#define ion_phys_addr_t unsigned long +#define ion_virt_addr_t unsigned long + +/** + * struct ion_platform_heap - defines a heap in the given platform + * @type: type of the heap from ion_heap_type enum + * @id: unique identifier for heap. When allocating (lower numbers + * will be allocated from first) + * @name: used for debug purposes + * @base: base address of heap in physical memory if applicable + * @size: size of the heap in bytes if applicable + * @memory_type:Memory type used for the heap + * @has_outer_cache: set to 1 if outer cache is used, 0 otherwise. + * @extra_data: Extra data specific to each heap type + */ +struct ion_platform_heap { + enum ion_heap_type type; + unsigned int id; + const char *name; + ion_phys_addr_t base; + size_t size; + enum ion_memory_types memory_type; + unsigned int has_outer_cache; + void *extra_data; +}; + +/** + * struct ion_cp_heap_pdata - defines a content protection heap in the given + * platform + * @permission_type: Memory ID used to identify the memory to TZ + * @align: Alignment requirement for the memory + * @secure_base: Base address for securing the heap. + * Note: This might be different from actual base address + * of this heap in the case of a shared heap. + * @secure_size: Memory size for securing the heap. + * Note: This might be different from actual size + * of this heap in the case of a shared heap. + * @reusable Flag indicating whether this heap is reusable of not. + * (see FMEM) + * @mem_is_fmem Flag indicating whether this memory is coming from fmem + * or not. + * @fixed_position If nonzero, position in the fixed area. + * @virt_addr: Virtual address used when using fmem. + * @iommu_map_all: Indicates whether we should map whole heap into IOMMU. + * @iommu_2x_map_domain: Indicates the domain to use for overmapping. + * @request_region: function to be called when the number of allocations + * goes from 0 -> 1 + * @release_region: function to be called when the number of allocations + * goes from 1 -> 0 + * @setup_region: function to be called upon ion registration + * + */ +struct ion_cp_heap_pdata { + enum ion_permission_type permission_type; + unsigned int align; + ion_phys_addr_t secure_base; /* Base addr used when heap is shared */ + size_t secure_size; /* Size used for securing heap when heap is shared*/ + int reusable; + int mem_is_fmem; + enum ion_fixed_position fixed_position; + int iommu_map_all; + int iommu_2x_map_domain; + ion_virt_addr_t *virt_addr; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); +}; + +/** + * struct ion_co_heap_pdata - defines a carveout heap in the given platform + * @adjacent_mem_id: Id of heap that this heap must be adjacent to. + * @align: Alignment requirement for the memory + * @mem_is_fmem Flag indicating whether this memory is coming from fmem + * or not. + * @fixed_position If nonzero, position in the fixed area. + * @request_region: function to be called when the number of allocations + * goes from 0 -> 1 + * @release_region: function to be called when the number of allocations + * goes from 1 -> 0 + * @setup_region: function to be called upon ion registration + * + */ +struct ion_co_heap_pdata { + int adjacent_mem_id; + unsigned int align; + int mem_is_fmem; + enum ion_fixed_position fixed_position; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); +}; + +/** + * struct ion_platform_data - array of platform heaps passed from board file + * @has_outer_cache: set to 1 if outer cache is used, 0 otherwise. + * @nr: number of structures in the array + * @request_region: function to be called when the number of allocations goes + * from 0 -> 1 + * @release_region: function to be called when the number of allocations goes + * from 1 -> 0 + * @setup_region: function to be called upon ion registration + * @heaps: array of platform_heap structions + * + * Provided by the board file in the form of platform data to a platform device. + */ +struct ion_platform_data { + unsigned int has_outer_cache; + int nr; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); + struct ion_platform_heap heaps[]; +}; + +#ifdef CONFIG_ION + +/** + * ion_reserve() - reserve memory for ion heaps if applicable + * @data: platform data specifying starting physical address and + * size + * + * Calls memblock reserve to set aside memory for heaps that are + * located at specific memory addresses or of specfic sizes not + * managed by the kernel + */ +void ion_reserve(struct ion_platform_data *data); + +/** + * ion_client_create() - allocate a client and returns it + * @dev: the global ion device + * @heap_mask: mask of heaps this client can allocate from + * @name: used for debugging + */ +struct ion_client *ion_client_create(struct ion_device *dev, + unsigned int heap_mask, const char *name); + +/** + * msm_ion_client_create - allocate a client using the ion_device specified in + * drivers/gpu/ion/msm/msm_ion.c + * + * heap_mask and name are the same as ion_client_create, return values + * are the same as ion_client_create. + */ + +struct ion_client *msm_ion_client_create(unsigned int heap_mask, + const char *name); + +/** + * ion_client_destroy() - free's a client and all it's handles + * @client: the client + * + * Free the provided client and all it's resources including + * any handles it is holding. + */ +void ion_client_destroy(struct ion_client *client); + +/** + * ion_alloc - allocate ion memory + * @client: the client + * @len: size of the allocation + * @align: requested allocation alignment, lots of hardware blocks have + * alignment requirements of some kind + * @flags: mask of heaps to allocate from, if multiple bits are set + * heaps will be tried in order from lowest to highest order bit + * + * Allocate memory in one of the heaps provided in heap mask and return + * an opaque handle to it. + */ +struct ion_handle *ion_alloc(struct ion_client *client, size_t len, + size_t align, unsigned int flags); + +/** + * ion_free - free a handle + * @client: the client + * @handle: the handle to free + * + * Free the provided handle. + */ +void ion_free(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_phys - returns the physical address and len of a handle + * @client: the client + * @handle: the handle + * @addr: a pointer to put the address in + * @len: a pointer to put the length in + * + * This function queries the heap for a particular handle to get the + * handle's physical address. It't output is only correct if + * a heap returns physically contiguous memory -- in other cases + * this api should not be implemented -- ion_sg_table should be used + * instead. Returns -EINVAL if the handle is invalid. This has + * no implications on the reference counting of the handle -- + * the returned value may not be valid if the caller is not + * holding a reference. + */ +int ion_phys(struct ion_client *client, struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + +/** + * ion_map_dma - return an sg_table describing a handle + * @client: the client + * @handle: the handle + * + * This function returns the sg_table describing + * a particular ion handle. + */ +struct sg_table *ion_sg_table(struct ion_client *client, + struct ion_handle *handle); + +/** + * ion_map_kernel - create mapping for the given handle + * @client: the client + * @handle: handle to map + * @flags: flags for this mapping + * + * Map the given handle into the kernel and return a kernel address that + * can be used to access this address. If no flags are specified, this + * will return a non-secure uncached mapping. + */ +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, + unsigned long flags); + +/** + * ion_unmap_kernel() - destroy a kernel mapping for a handle + * @client: the client + * @handle: handle to unmap + */ +void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_share_dma_buf() - given an ion client, create a dma-buf fd + * @client: the client + * @handle: the handle + */ +int ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle + * @client: the client + * @fd: the dma-buf fd + * + * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, + * import that fd and return a handle representing it. If a dma-buf from + * another exporter is passed in this function will return ERR_PTR(-EINVAL) + */ +struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); + +/** + * ion_handle_get_flags - get the flags for a given handle + * + * @client - client who allocated the handle + * @handle - handle to get the flags + * @flags - pointer to store the flags + * + * Gets the current flags for a handle. These flags indicate various options + * of the buffer (caching, security, etc.) + */ +int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, + unsigned long *flags); + + +/** + * ion_map_iommu - map the given handle into an iommu + * + * @client - client who allocated the handle + * @handle - handle to map + * @domain_num - domain number to map to + * @partition_num - partition number to allocate iova from + * @align - alignment for the iova + * @iova_length - length of iova to map. If the iova length is + * greater than the handle length, the remaining + * address space will be mapped to a dummy buffer. + * @iova - pointer to store the iova address + * @buffer_size - pointer to store the size of the buffer + * @flags - flags for options to map + * @iommu_flags - flags specific to the iommu. + * + * Maps the handle into the iova space specified via domain number. Iova + * will be allocated from the partition specified via partition_num. + * Returns 0 on success, negative value on error. + */ +int ion_map_iommu(struct ion_client *client, struct ion_handle *handle, + int domain_num, int partition_num, unsigned long align, + unsigned long iova_length, unsigned long *iova, + unsigned long *buffer_size, + unsigned long flags, unsigned long iommu_flags); + + +/** + * ion_handle_get_size - get the allocated size of a given handle + * + * @client - client who allocated the handle + * @handle - handle to get the size + * @size - pointer to store the size + * + * gives the allocated size of a handle. returns 0 on success, negative + * value on error + * + * NOTE: This is intended to be used only to get a size to pass to map_iommu. + * You should *NOT* rely on this for any other usage. + */ + +int ion_handle_get_size(struct ion_client *client, struct ion_handle *handle, + unsigned long *size); + +/** + * ion_unmap_iommu - unmap the handle from an iommu + * + * @client - client who allocated the handle + * @handle - handle to unmap + * @domain_num - domain to unmap from + * @partition_num - partition to unmap from + * + * Decrement the reference count on the iommu mapping. If the count is + * 0, the mapping will be removed from the iommu. + */ +void ion_unmap_iommu(struct ion_client *client, struct ion_handle *handle, + int domain_num, int partition_num); + + +/** + * ion_secure_heap - secure a heap + * + * @client - a client that has allocated from the heap heap_id + * @heap_id - heap id to secure. + * @version - version of content protection + * @data - extra data needed for protection + * + * Secure a heap + * Returns 0 on success + */ +int ion_secure_heap(struct ion_device *dev, int heap_id, int version, + void *data); + +/** + * ion_unsecure_heap - un-secure a heap + * + * @client - a client that has allocated from the heap heap_id + * @heap_id - heap id to un-secure. + * @version - version of content protection + * @data - extra data needed for protection + * + * Un-secure a heap + * Returns 0 on success + */ +int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version, + void *data); + +/** + * msm_ion_secure_heap - secure a heap. Wrapper around ion_secure_heap. + * + * @heap_id - heap id to secure. + * + * Secure a heap + * Returns 0 on success + */ +int msm_ion_secure_heap(int heap_id); + +/** + * msm_ion_unsecure_heap - unsecure a heap. Wrapper around ion_unsecure_heap. + * + * @heap_id - heap id to secure. + * + * Un-secure a heap + * Returns 0 on success + */ +int msm_ion_unsecure_heap(int heap_id); + +/** + * msm_ion_secure_heap_2_0 - secure a heap using 2.0 APIs + * Wrapper around ion_secure_heap. + * + * @heap_id - heap id to secure. + * @usage - usage hint to TZ + * + * Secure a heap + * Returns 0 on success + */ +int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage); + +/** + * msm_ion_unsecure_heap - unsecure a heap secured with 3.0 APIs. + * Wrapper around ion_unsecure_heap. + * + * @heap_id - heap id to secure. + * @usage - usage hint to TZ + * + * Un-secure a heap + * Returns 0 on success + */ +int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage); + +/** + * msm_ion_do_cache_op - do cache operations. + * + * @client - pointer to ION client. + * @handle - pointer to buffer handle. + * @vaddr - virtual address to operate on. + * @len - Length of data to do cache operation on. + * @cmd - Cache operation to perform: + * ION_IOC_CLEAN_CACHES + * ION_IOC_INV_CACHES + * ION_IOC_CLEAN_INV_CACHES + * + * Returns 0 on success + */ +int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *vaddr, unsigned long len, unsigned int cmd); + +#else +static inline void ion_reserve(struct ion_platform_data *data) +{ + +} + +static inline struct ion_client *ion_client_create(struct ion_device *dev, + unsigned int heap_mask, const char *name) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ion_client *msm_ion_client_create(unsigned int heap_mask, + const char *name) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_client_destroy(struct ion_client *client) { } + +static inline struct ion_handle *ion_alloc(struct ion_client *client, + size_t len, size_t align, unsigned int flags) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_free(struct ion_client *client, + struct ion_handle *handle) { } + + +static inline int ion_phys(struct ion_client *client, + struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len) +{ + return -ENODEV; +} + +static inline struct sg_table *ion_sg_table(struct ion_client *client, + struct ion_handle *handle) +{ + return ERR_PTR(-ENODEV); +} + +static inline void *ion_map_kernel(struct ion_client *client, + struct ion_handle *handle, unsigned long flags) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_unmap_kernel(struct ion_client *client, + struct ion_handle *handle) { } + +static inline int ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle) +{ + return -ENODEV; +} + +static inline struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) +{ + return ERR_PTR(-ENODEV); +} + +static inline int ion_handle_get_flags(struct ion_client *client, + struct ion_handle *handle, unsigned long *flags) +{ + return -ENODEV; +} + +static inline int ion_map_iommu(struct ion_client *client, + struct ion_handle *handle, int domain_num, + int partition_num, unsigned long align, + unsigned long iova_length, unsigned long *iova, + unsigned long *buffer_size, + unsigned long flags, + unsigned long iommu_flags) +{ + return -ENODEV; +} + +static inline void ion_unmap_iommu(struct ion_client *client, + struct ion_handle *handle, int domain_num, + int partition_num) +{ + return; +} + +static inline int ion_secure_heap(struct ion_device *dev, int heap_id, + int version, void *data) +{ + return -ENODEV; + +} + +static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id, + int version, void *data) +{ + return -ENODEV; +} + +static inline int msm_ion_secure_heap(int heap_id) +{ + return -ENODEV; + +} + +static inline int msm_ion_unsecure_heap(int heap_id) +{ + return -ENODEV; +} + +static inline int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage) +{ + return -ENODEV; +} + +static inline int msm_ion_unsecure_heap_2_0(int heap_id, + enum cp_mem_usage usage) +{ + return -ENODEV; +} + +static inline int msm_ion_do_cache_op(struct ion_client *client, + struct ion_handle *handle, void *vaddr, + unsigned long len, unsigned int cmd) +{ + return -ENODEV; +} + +#endif /* CONFIG_ION */ +#endif /* __KERNEL__ */ + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @align: required alignment of the allocation + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to refer + * to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_allocation_data { + size_t len; + size_t align; + unsigned int flags; + struct ion_handle *handle; +}; + +/** + * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair + * @handle: a handle + * @fd: a file descriptor representing that handle + * + * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with + * the handle returned from ion alloc, and the kernel returns the file + * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace + * provides the file descriptor and the kernel returns the handle. + */ +struct ion_fd_data { + struct ion_handle *handle; + int fd; +}; + +/** + * struct ion_handle_data - a handle passed to/from the kernel + * @handle: a handle + */ +struct ion_handle_data { + struct ion_handle *handle; +}; + +/** + * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl + * @cmd: the custom ioctl function to call + * @arg: additional data to pass to the custom ioctl, typically a user + * pointer to a predefined structure + * + * This works just like the regular cmd and arg fields of an ioctl. + */ +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + + +/* struct ion_flush_data - data passed to ion for flushing caches + * + * @handle: handle with data to flush + * @fd: fd to flush + * @vaddr: userspace virtual address mapped with mmap + * @offset: offset into the handle to flush + * @length: length of handle to flush + * + * Performs cache operations on the handle. If p is the start address + * of the handle, p + offset through p + offset + length will have + * the cache operations performed + */ +struct ion_flush_data { + struct ion_handle *handle; + int fd; + void *vaddr; + unsigned int offset; + unsigned int length; +}; + +/* struct ion_flag_data - information about flags for this buffer + * + * @handle: handle to get flags from + * @flags: flags of this handle + * + * Takes handle as an input and outputs the flags from the handle + * in the flag field. + */ +struct ion_flag_data { + struct ion_handle *handle; + unsigned long flags; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +/** + * DOC: ION_IOC_FREE - free memory + * + * Takes an ion_handle_data struct and frees the handle. + */ +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +/** + * DOC: ION_IOC_MAP - get a file descriptor to mmap + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be used as an argument to mmap. + */ +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +/** + * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be passed to another process. The corresponding opaque handle can + * be retrieved via ION_IOC_IMPORT. + */ +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +/** + * DOC: ION_IOC_IMPORT - imports a shared file descriptor + * + * Takes an ion_fd_data struct with the fd field populated with a valid file + * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle + * filed set to the corresponding opaque handle. + */ +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, int) + +/** + * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl + * + * Takes the argument of the architecture specific ioctl to call and + * passes appropriate userdata for that ioctl + */ +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + + +/** + * DOC: ION_IOC_CLEAN_CACHES - clean the caches + * + * Clean the caches of the handle specified. + */ +#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MAGIC, 7, \ + struct ion_flush_data) +/** + * DOC: ION_MSM_IOC_INV_CACHES - invalidate the caches + * + * Invalidate the caches of the handle specified. + */ +#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MAGIC, 8, \ + struct ion_flush_data) +/** + * DOC: ION_MSM_IOC_CLEAN_CACHES - clean and invalidate the caches + * + * Clean and invalidate the caches of the handle specified. + */ +#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MAGIC, 9, \ + struct ion_flush_data) + +/** + * DOC: ION_IOC_GET_FLAGS - get the flags of the handle + * + * Gets the flags of the current handle which indicate cachability, + * secure state etc. + */ +#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MAGIC, 10, \ + struct ion_flag_data) +#endif /* _LINUX_ION_H */ diff --git a/modetest/libmsm.c b/modetest/libmsm.c new file mode 100755 index 0000000..e92340f --- /dev/null +++ b/modetest/libmsm.c @@ -0,0 +1,138 @@ +/************************************************************************** + * + * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +//#include "config.h" +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "msmdumb_priv.h" + +int msm_create(int fd, struct msm_driver **out) +{ + return msmdumb_create(fd, out); +} + +int msm_get_prop(struct msm_driver *msm, unsigned key, unsigned *out) +{ + switch (key) { + case MSM_BO_TYPE: + break; + default: + return -EINVAL; + } + return msm->get_prop(msm, key, out); +} + +int msm_destroy(struct msm_driver **msm) +{ + if (!(*msm)) + return 0; + + free(*msm); + *msm = NULL; + return 0; +} + +int msm_bo_create(struct msm_driver *msm, const unsigned *attr, struct msm_bo **out, int buf_fd) +{ + unsigned width = 0; + unsigned height = 0; + enum msm_bo_type type = MSM_BO_TYPE_SCANOUT_X8R8G8B8; + int i; + + for (i = 0; attr[i];) { + unsigned key = attr[i++]; + unsigned value = attr[i++]; + + switch (key) { + case MSM_WIDTH: + width = value; + break; + case MSM_HEIGHT: + height = value; + break; + case MSM_BO_TYPE: + type = value; + break; + default: + return -EINVAL; + } + } + + if (width == 0 || height == 0) + return -EINVAL; + + /* XXX sanity check type */ + + if (type == MSM_BO_TYPE_CURSOR_64X64_A8R8G8B8 && + (width != 64 || height != 64)) + return -EINVAL; + + return msm->bo_create(msm, width, height, type, attr, out, buf_fd); +} + +int msm_bo_get_prop(struct msm_bo *bo, unsigned key, unsigned *out) +{ + switch (key) { + case MSM_PITCH: + *out = bo->pitch; + break; + case MSM_HANDLE: + *out = bo->handle; + break; + default: + return -EINVAL; + } + + return 0; +} + +int msm_bo_map(struct msm_bo *bo, void **out) +{ + return bo->msm->bo_map(bo, out); +} + +int msm_bo_unmap(struct msm_bo *bo) +{ + return bo->msm->bo_unmap(bo); +} + +int msm_bo_destroy(struct msm_bo **bo) +{ + int ret; + + if (!(*bo)) + return 0; + + ret = (*bo)->msm->bo_destroy(*bo); + if (ret) + return ret; + + *bo = NULL; + return 0; +} diff --git a/modetest/libmsm.h b/modetest/libmsm.h new file mode 100755 index 0000000..fde92bf --- /dev/null +++ b/modetest/libmsm.h @@ -0,0 +1,82 @@ +/************************************************************************** + * + * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef _LIBMSM_H_ +#define _LIBMSM_H_ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/** + * \file + * + */ + +struct msm_driver; +struct msm_bo; + +enum msm_attrib +{ + MSM_TERMINATE_PROP_LIST, +#define MSM_TERMINATE_PROP_LIST MSM_TERMINATE_PROP_LIST + MSM_BO_TYPE, +#define MSM_BO_TYPE MSM_BO_TYPE + MSM_WIDTH, +#define MSM_WIDTH MSM_WIDTH + MSM_HEIGHT, +#define MSM_HEIGHT MSM_HEIGHT + MSM_PITCH, +#define MSM_PITCH MSM_PITCH + MSM_HANDLE, +#define MSM_HANDLE MSM_HANDLE +}; + +enum msm_bo_type +{ + MSM_BO_TYPE_SCANOUT_X8R8G8B8 = (1 << 0), +#define MSM_BO_TYPE_SCANOUT_X8R8G8B8 MSM_BO_TYPE_SCANOUT_X8R8G8B8 + MSM_BO_TYPE_CURSOR_64X64_A8R8G8B8 = (1 << 1), +#define MSM_BO_TYPE_CURSOR_64X64_A8R8G8B8 MSM_BO_TYPE_CURSOR_64X64_A8R8G8B8 +}; + +int msm_create(int fd, struct msm_driver **out); +int msm_get_prop(struct msm_driver *msm, unsigned key, unsigned *out); +int msm_destroy(struct msm_driver **msm); + +int msm_bo_create(struct msm_driver *msm, const unsigned *attr, struct msm_bo **out, int buf_fd); +int msm_bo_get_prop(struct msm_bo *bo, unsigned key, unsigned *out); +int msm_bo_map(struct msm_bo *bo, void **out); +int msm_bo_unmap(struct msm_bo *bo); +int msm_bo_destroy(struct msm_bo **bo); + +#if defined(__cplusplus) || defined(c_plusplus) +}; +#endif + +#endif diff --git a/modetest/msmdumb.c b/modetest/msmdumb.c new file mode 100755 index 0000000..312a0fb --- /dev/null +++ b/modetest/msmdumb.c @@ -0,0 +1,217 @@ +/************************************************************************** + * + * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#define HAVE_STDINT_H +#define _FILE_OFFSET_BITS 64 + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "msmdumb_priv.h" + +#include <sys/mman.h> +#include <sys/ioctl.h> +#include "xf86drm.h" + +#include "i915_drm.h" + +struct dumb_bo +{ + struct msm_bo base; + unsigned map_count; +}; + +static int +dumb_get_prop(struct msm_driver *msm, unsigned key, unsigned *out) +{ + switch (key) { + case MSM_BO_TYPE: + *out = MSM_BO_TYPE_SCANOUT_X8R8G8B8 | MSM_BO_TYPE_CURSOR_64X64_A8R8G8B8; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +dumb_destroy(struct msm_driver *msm) +{ + free(msm); + return 0; +} + +static int +dumb_bo_create(struct msm_driver *msm, + const unsigned width, const unsigned height, + const enum msm_bo_type type, const unsigned *attr, + struct msm_bo **out, int buf_fd) +{ + struct drm_mode_create_dumb arg; + struct dumb_bo *bo; + int i, ret; + uint32_t prime_handle = 0; + + for (i = 0; attr[i]; i += 2) { + switch (attr[i]) { + case MSM_WIDTH: + case MSM_HEIGHT: + break; + case MSM_BO_TYPE: + break; + default: + return -EINVAL; + } + } + + bo = calloc(1, sizeof(*bo)); + if (!bo) + return -ENOMEM; + + memset(&arg, 0, sizeof(arg)); + + /* All BO_TYPE currently are 32bpp formats */ + arg.bpp = 32; + arg.width = width; + arg.height = height; + + ret = drmPrimeFDToHandle(msm->fd, (uint32_t) buf_fd, &prime_handle); + + if (ret) { + fprintf(stderr, "\n drmPrimeHandleToFD failed with error %d ", ret); + goto err_free; + } + + bo->base.handle = (unsigned) prime_handle; + bo->base.msm = msm; +#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1)) + bo->base.pitch = ((arg.bpp + 7)/8) * (ALIGN(width, 32)); + bo->base.size = ALIGN(bo->base.pitch * height, 0x1000); + + *out = &bo->base; + + return 0; + +err_free: + free(bo); + return ret; +} + +static int +dumb_bo_get_prop(struct msm_bo *bo, unsigned key, unsigned *out) +{ + switch (key) { + default: + return -EINVAL; + } +} + +static int +dumb_bo_map(struct msm_bo *_bo, void **out) +{ + struct dumb_bo *bo = (struct dumb_bo *)_bo; + struct drm_mode_map_dumb arg; + void *map = NULL; + int ret; + + if (bo->base.ptr) { + bo->map_count++; + *out = bo->base.ptr; + return 0; + } + + memset(&arg, 0, sizeof(arg)); + arg.handle = bo->base.handle; + + ret = drmIoctl(bo->base.msm->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg); + if (ret) + return ret; + + map = mmap(0, bo->base.size, PROT_READ | PROT_WRITE, MAP_SHARED, bo->base.msm->fd, arg.offset); + if (map == MAP_FAILED) + return -errno; + + bo->base.ptr = map; + bo->map_count++; + *out = bo->base.ptr; + + return 0; +} + +static int +dumb_bo_unmap(struct msm_bo *_bo) +{ + struct dumb_bo *bo = (struct dumb_bo *)_bo; + bo->map_count--; + return 0; +} + +static int +dumb_bo_destroy(struct msm_bo *_bo) +{ + struct dumb_bo *bo = (struct dumb_bo *)_bo; + + if (bo->base.ptr) { + /* XXX Sanity check map_count */ + munmap(bo->base.ptr, bo->base.size); + bo->base.ptr = NULL; + } + + free(bo); + return 0; +} + +int +msmdumb_create(int fd, struct msm_driver **out) +{ + struct msm_driver *msm; + int ret; + uint64_t cap = 0; + + ret = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &cap); + if (ret || cap == 0) + return -EINVAL; + + msm = calloc(1, sizeof(*msm)); + if (!msm) + return -ENOMEM; + + msm->fd = fd; + + msm->bo_create = dumb_bo_create; + msm->bo_map = dumb_bo_map; + msm->bo_unmap = dumb_bo_unmap; + msm->bo_get_prop = dumb_bo_get_prop; + msm->bo_destroy = dumb_bo_destroy; + msm->get_prop = dumb_get_prop; + msm->destroy = dumb_destroy; + *out = msm; + + return 0; +} diff --git a/modetest/msmdumb_priv.h b/modetest/msmdumb_priv.h new file mode 100755 index 0000000..67cd90b --- /dev/null +++ b/modetest/msmdumb_priv.h @@ -0,0 +1,68 @@ +/************************************************************************** + * + * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MSMDUMBPRIV_H_ +#define MSMDUMBPRIV_H_ + +#include "libmsm.h" + +struct msm_driver +{ + int (*get_prop)(struct msm_driver *kms, const unsigned key, + unsigned *out); + int (*destroy)(struct msm_driver *kms); + + int (*bo_create)(struct msm_driver *kms, + unsigned width, + unsigned height, + enum msm_bo_type type, + const unsigned *attr, + struct msm_bo **out, + int buf_fd); + int (*bo_get_prop)(struct msm_bo *bo, const unsigned key, + unsigned *out); + int (*bo_map)(struct msm_bo *bo, void **out); + int (*bo_unmap)(struct msm_bo *bo); + int (*bo_destroy)(struct msm_bo *bo); + + int fd; +}; + +struct msm_bo +{ + struct msm_driver *msm; + void *ptr; + size_t size; + size_t offset; + size_t pitch; + unsigned handle; +}; + +int msmdumb_create(int fd, struct msm_driver **out); + +#endif diff --git a/modetest/msmmodetest.c b/modetest/msmmodetest.c new file mode 100755 index 0000000..0d8de7d --- /dev/null +++ b/modetest/msmmodetest.c @@ -0,0 +1,1564 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz <jakob@tungstengraphics.com> + * Copyright 2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This fairly simple test program dumps output in a similar format to the + * "xrandr" tool everyone knows & loves. It's necessarily slightly different + * since the kernel separates outputs into encoder and connector structures, + * each with their own unique ID. The program also allows test testing of the + * memory management and mode setting APIs by allowing the user to specify a + * connector and mode to use for mode setting. If all works as expected, a + * blue background should be painted on the monitor attached to the specified + * connector after the selected mode is set. + * + * TODO: use cairo to write the mode info on the selected output once + * the mode has been programmed, along with possible test patterns. + */ +//#include "config.h" + +#include <assert.h> +#include <ctype.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/poll.h> +#include <sys/time.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" +#include "drm_fourcc.h" +#include "libmsm.h" + +#include "buffers.h" + +struct crtc { + drmModeCrtc *crtc; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; + drmModeModeInfo *mode; +}; + +struct encoder { + drmModeEncoder *encoder; +}; + +struct connector { + drmModeConnector *connector; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +}; + +struct fb { + drmModeFB *fb; +}; + +struct plane { + drmModePlane *plane; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +}; + +struct resources { + drmModeRes *res; + drmModePlaneRes *plane_res; + + struct crtc *crtcs; + struct encoder *encoders; + struct connector *connectors; + struct fb *fbs; + struct plane *planes; +}; + +struct device { + int fd; + + struct resources *resources; + struct msm_driver *msm; + + struct { + unsigned int width; + unsigned int height; + + unsigned int fb_id; + struct msm_bo *bo; + } mode; +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct type_name { + int type; + const char *name; +}; + +#define type_name_fn(res) \ +const char * res##_str(int type) { \ + unsigned int i; \ + for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ + if (res##_names[i].type == type) \ + return res##_names[i].name; \ + } \ + return "(invalid)"; \ +} + +struct type_name encoder_type_names[] = { + { DRM_MODE_ENCODER_NONE, "none" }, + { DRM_MODE_ENCODER_DAC, "DAC" }, + { DRM_MODE_ENCODER_TMDS, "TMDS" }, + { DRM_MODE_ENCODER_LVDS, "LVDS" }, + { DRM_MODE_ENCODER_TVDAC, "TVDAC" }, +}; + +static type_name_fn(encoder_type) + +struct type_name connector_status_names[] = { + { DRM_MODE_CONNECTED, "connected" }, + { DRM_MODE_DISCONNECTED, "disconnected" }, + { DRM_MODE_UNKNOWNCONNECTION, "unknown" }, +}; + +static type_name_fn(connector_status) + +struct type_name connector_type_names[] = { + { DRM_MODE_CONNECTOR_Unknown, "unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "s-video" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, + { DRM_MODE_CONNECTOR_TV, "TV" }, + { DRM_MODE_CONNECTOR_eDP, "eDP" }, +}; + +static type_name_fn(connector_type) + +#define bit_name_fn(res) \ +const char * res##_str(int type) { \ + unsigned int i; \ + const char *sep = ""; \ + for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ + if (type & (1 << i)) { \ + printf("%s%s", sep, res##_names[i]); \ + sep = ", "; \ + } \ + } \ + return NULL; \ +} + +static const char *mode_type_names[] = { + "builtin", + "clock_c", + "crtc_c", + "preferred", + "default", + "userdef", + "driver", +}; + +static bit_name_fn(mode_type) + +static const char *mode_flag_names[] = { + "phsync", + "nhsync", + "pvsync", + "nvsync", + "interlace", + "dblscan", + "csync", + "pcsync", + "ncsync", + "hskew", + "bcast", + "pixmux", + "dblclk", + "clkdiv2" +}; + +static bit_name_fn(mode_flag) + +static void dump_encoders(struct device *dev) +{ + drmModeEncoder *encoder; + int i; + + printf("Encoders:\n"); + printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n"); + for (i = 0; i < dev->resources->res->count_encoders; i++) { + encoder = dev->resources->encoders[i].encoder; + if (!encoder) + continue; + + printf("%d\t%d\t%s\t0x%08x\t0x%08x\n", + encoder->encoder_id, + encoder->crtc_id, + encoder_type_str(encoder->encoder_type), + encoder->possible_crtcs, + encoder->possible_clones); + } + printf("\n"); +} + +static void dump_mode(drmModeModeInfo *mode) +{ + printf(" %s %d %d %d %d %d %d %d %d %d", + mode->name, + mode->vrefresh, + mode->hdisplay, + mode->hsync_start, + mode->hsync_end, + mode->htotal, + mode->vdisplay, + mode->vsync_start, + mode->vsync_end, + mode->vtotal); + + printf(" flags: "); + mode_flag_str(mode->flags); + printf("; type: "); + mode_type_str(mode->type); + printf("\n"); +} + +static void dump_blob(struct device *dev, uint32_t blob_id) +{ + uint32_t i; + unsigned char *blob_data; + drmModePropertyBlobPtr blob; + + blob = drmModeGetPropertyBlob(dev->fd, blob_id); + if (!blob) + return; + + blob_data = blob->data; + + for (i = 0; i < blob->length; i++) { + if (i % 16 == 0) + printf("\n\t\t\t"); + printf("%.2hhx", blob_data[i]); + } + printf("\n"); + + drmModeFreePropertyBlob(blob); +} + +static void dump_prop(struct device *dev, drmModePropertyPtr prop, + uint32_t prop_id, uint64_t value) +{ + int i; + printf("\t%d", prop_id); + if (!prop) { + printf("\n"); + return; + } + + printf(" %s:\n", prop->name); + + printf("\t\tflags:"); + if (prop->flags & DRM_MODE_PROP_PENDING) + printf(" pending"); + if (prop->flags & DRM_MODE_PROP_RANGE) + printf(" range"); + if (prop->flags & DRM_MODE_PROP_IMMUTABLE) + printf(" immutable"); + if (prop->flags & DRM_MODE_PROP_ENUM) + printf(" enum"); + if (prop->flags & DRM_MODE_PROP_BITMASK) + printf(" bitmask"); + if (prop->flags & DRM_MODE_PROP_BLOB) + printf(" blob"); + printf("\n"); + + if (prop->flags & DRM_MODE_PROP_RANGE) { + printf("\t\tvalues:"); + for (i = 0; i < prop->count_values; i++) + printf(" %"PRIu64, prop->values[i]); + printf("\n"); + } + + if (prop->flags & DRM_MODE_PROP_ENUM) { + printf("\t\tenums:"); + for (i = 0; i < prop->count_enums; i++) + printf(" %s=%llu", prop->enums[i].name, + prop->enums[i].value); + printf("\n"); + } else if (prop->flags & DRM_MODE_PROP_BITMASK) { + printf("\t\tvalues:"); + for (i = 0; i < prop->count_enums; i++) + printf(" %s=0x%llx", prop->enums[i].name, + (1LL << prop->enums[i].value)); + printf("\n"); + } else { + assert(prop->count_enums == 0); + } + + if (prop->flags & DRM_MODE_PROP_BLOB) { + printf("\t\tblobs:\n"); + for (i = 0; i < prop->count_blobs; i++) + dump_blob(dev, prop->blob_ids[i]); + printf("\n"); + } else { + assert(prop->count_blobs == 0); + } + + printf("\t\tvalue:"); + if (prop->flags & DRM_MODE_PROP_BLOB) + dump_blob(dev, value); + else + printf(" %"PRIu64"\n", value); +} + +static void dump_connectors(struct device *dev) +{ + int i, j; + + printf("Connectors:\n"); + printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n"); + for (i = 0; i < dev->resources->res->count_connectors; i++) { + struct connector *_connector = &dev->resources->connectors[i]; + drmModeConnector *connector = _connector->connector; + if (!connector) + continue; + + printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t", + connector->connector_id, + connector->encoder_id, + connector_status_str(connector->connection), + connector_type_str(connector->connector_type), + connector->mmWidth, connector->mmHeight, + connector->count_modes); + + for (j = 0; j < connector->count_encoders; j++) + printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]); + printf("\n"); + + if (connector->count_modes) { + printf(" modes:\n"); + printf("\tname refresh (Hz) hdisp hss hse htot vdisp " + "vss vse vtot)\n"); + for (j = 0; j < connector->count_modes; j++) + dump_mode(&connector->modes[j]); + } + + if (_connector->props) { + printf(" props:\n"); + for (j = 0; j < (int)_connector->props->count_props; j++) + dump_prop(dev, _connector->props_info[j], + _connector->props->props[j], + _connector->props->prop_values[j]); + } + } + printf("\n"); +} + +static void dump_crtcs(struct device *dev) +{ + int i; + uint32_t j; + + printf("CRTCs:\n"); + printf("id\tfb\tpos\tsize\n"); + for (i = 0; i < dev->resources->res->count_crtcs; i++) { + struct crtc *_crtc = &dev->resources->crtcs[i]; + drmModeCrtc *crtc = _crtc->crtc; + if (!crtc) + continue; + + printf("%d\t%d\t(%d,%d)\t(%dx%d)\n", + crtc->crtc_id, + crtc->buffer_id, + crtc->x, crtc->y, + crtc->width, crtc->height); + dump_mode(&crtc->mode); + + if (_crtc->props) { + printf(" props:\n"); + for (j = 0; j < _crtc->props->count_props; j++) + dump_prop(dev, _crtc->props_info[j], + _crtc->props->props[j], + _crtc->props->prop_values[j]); + } else { + printf(" no properties found\n"); + } + } + printf("\n"); +} + +static void dump_framebuffers(struct device *dev) +{ + drmModeFB *fb; + int i; + + printf("Frame buffers:\n"); + printf("id\tsize\tpitch\n"); + for (i = 0; i < dev->resources->res->count_fbs; i++) { + fb = dev->resources->fbs[i].fb; + if (!fb) + continue; + + printf("%u\t(%ux%u)\t%u\n", + fb->fb_id, + fb->width, fb->height, + fb->pitch); + } + printf("\n"); +} + +static void dump_planes(struct device *dev) +{ + unsigned int i, j; + + printf("Planes:\n"); + printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n"); + + if (!dev->resources->plane_res) + return; + + for (i = 0; i < dev->resources->plane_res->count_planes; i++) { + struct plane *plane = &dev->resources->planes[i]; + drmModePlane *ovr = plane->plane; + if (!ovr) + continue; + + printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n", + ovr->plane_id, ovr->crtc_id, ovr->fb_id, + ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, + ovr->gamma_size, ovr->possible_crtcs); + + if (!ovr->count_formats) + continue; + + printf(" formats:"); + for (j = 0; j < ovr->count_formats; j++) + printf(" %4.4s", (char *)&ovr->formats[j]); + printf("\n"); + + if (plane->props) { + printf(" props:\n"); + for (j = 0; j < plane->props->count_props; j++) + dump_prop(dev, plane->props_info[j], + plane->props->props[j], + plane->props->prop_values[j]); + } else { + printf(" no properties found\n"); + } + } + printf("\n"); + + return; +} + +static void free_resources(struct resources *res) +{ + if (!res) + return; + +#define free_resource(_res, __res, type, Type) \ + do { \ + int i; \ + if (!(_res)->type##s) \ + break; \ + for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ + if (!(_res)->type##s[i].type) \ + break; \ + drmModeFree##Type((_res)->type##s[i].type); \ + } \ + free((_res)->type##s); \ + } while (0) + +#define free_properties(_res, __res, type) \ + do { \ + int i; \ + for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ + drmModeFreeObjectProperties(res->type##s[i].props); \ + free(res->type##s[i].props_info); \ + } \ + } while (0) + + if (res->res) { + free_properties(res, res, crtc); + + free_resource(res, res, crtc, Crtc); + free_resource(res, res, encoder, Encoder); + free_resource(res, res, connector, Connector); + free_resource(res, res, fb, FB); + + drmModeFreeResources(res->res); + } + + if (res->plane_res) { + free_properties(res, plane_res, plane); + + free_resource(res, plane_res, plane, Plane); + + drmModeFreePlaneResources(res->plane_res); + } + + free(res); +} + +static struct resources *get_resources(struct device *dev) +{ + struct resources *res; + int i; + + res = malloc(sizeof *res); + if (res == 0) + return NULL; + + memset(res, 0, sizeof *res); + + res->res = drmModeGetResources(dev->fd); + if (!res->res) { + fprintf(stderr, "drmModeGetResources failed: %s\n", + strerror(errno)); + goto error; + } + + res->crtcs = malloc(res->res->count_crtcs * sizeof *res->crtcs); + res->encoders = malloc(res->res->count_encoders * sizeof *res->encoders); + res->connectors = malloc(res->res->count_connectors * sizeof *res->connectors); + res->fbs = malloc(res->res->count_fbs * sizeof *res->fbs); + + if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) + goto error; + + memset(res->crtcs , 0, res->res->count_crtcs * sizeof *res->crtcs); + memset(res->encoders, 0, res->res->count_encoders * sizeof *res->encoders); + memset(res->connectors, 0, res->res->count_connectors * sizeof *res->connectors); + memset(res->fbs, 0, res->res->count_fbs * sizeof *res->fbs); + +#define get_resource(_res, __res, type, Type) \ + do { \ + int i; \ + for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ + (_res)->type##s[i].type = \ + drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \ + if (!(_res)->type##s[i].type) \ + fprintf(stderr, "could not get %s %i: %s\n", \ + #type, (_res)->__res->type##s[i], \ + strerror(errno)); \ + } \ + } while (0) + + get_resource(res, res, crtc, Crtc); + get_resource(res, res, encoder, Encoder); + get_resource(res, res, connector, Connector); + get_resource(res, res, fb, FB); + +#define get_properties(_res, __res, type, Type) \ + do { \ + int i; \ + for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ + struct type *obj = &res->type##s[i]; \ + unsigned int j; \ + obj->props = \ + drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \ + DRM_MODE_OBJECT_##Type); \ + if (!obj->props) { \ + fprintf(stderr, \ + "could not get %s %i properties: %s\n", \ + #type, obj->type->type##_id, \ + strerror(errno)); \ + continue; \ + } \ + obj->props_info = malloc(obj->props->count_props * \ + sizeof *obj->props_info); \ + if (!obj->props_info) \ + continue; \ + for (j = 0; j < obj->props->count_props; ++j) \ + obj->props_info[j] = \ + drmModeGetProperty(dev->fd, obj->props->props[j]); \ + } \ + } while (0) + + get_properties(res, res, crtc, CRTC); + get_properties(res, res, connector, CONNECTOR); + + for (i = 0; i < res->res->count_crtcs; ++i) + res->crtcs[i].mode = &res->crtcs[i].crtc->mode; + + res->plane_res = drmModeGetPlaneResources(dev->fd); + if (!res->plane_res) { + fprintf(stderr, "drmModeGetPlaneResources failed: %s\n", + strerror(errno)); + return res; + } + + res->planes = malloc(res->plane_res->count_planes * sizeof *res->planes); + if (!res->planes) + goto error; + + memset(res->planes, 0, res->plane_res->count_planes * sizeof *res->planes); + + get_resource(res, plane_res, plane, Plane); + get_properties(res, plane_res, plane, PLANE); + + return res; + +error: + free_resources(res); + return NULL; +} + +static int get_crtc_index(struct device *dev, uint32_t id) +{ + int i; + + for (i = 0; i < dev->resources->res->count_crtcs; ++i) { + drmModeCrtc *crtc = dev->resources->crtcs[i].crtc; + if (crtc && crtc->crtc_id == id) + return i; + } + + return -1; +} + +static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id) +{ + drmModeConnector *connector; + int i; + + for (i = 0; i < dev->resources->res->count_connectors; i++) { + connector = dev->resources->connectors[i].connector; + if (connector && connector->connector_id == id) + return connector; + } + + return NULL; +} + +static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id) +{ + drmModeEncoder *encoder; + int i; + + for (i = 0; i < dev->resources->res->count_encoders; i++) { + encoder = dev->resources->encoders[i].encoder; + if (encoder && encoder->encoder_id == id) + return encoder; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Pipes and planes + */ + +/* + * Mode setting with the kernel interfaces is a bit of a chore. + * First you have to find the connector in question and make sure the + * requested mode is available. + * Then you need to find the encoder attached to that connector so you + * can bind it with a free crtc. + */ +struct pipe_arg { + uint32_t *con_ids; + unsigned int num_cons; + uint32_t crtc_id; + char mode_str[64]; + char format_str[5]; + unsigned int vrefresh; + unsigned int fourcc; + drmModeModeInfo *mode; + struct crtc *crtc; + unsigned int fb_id[2], current_fb_id; + struct timeval start; + + int swap_count; +}; + +struct plane_arg { + uint32_t crtc_id; /* the id of CRTC to bind to */ + bool has_position; + int32_t x, y; + uint32_t w, h; + double scale; + unsigned int fb_id; + char format_str[5]; /* need to leave room for terminating \0 */ + unsigned int fourcc; +}; + +static drmModeModeInfo * +connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str, + const unsigned int vrefresh) +{ + drmModeConnector *connector; + drmModeModeInfo *mode; + int i; + + connector = get_connector_by_id(dev, con_id); + if (!connector || !connector->count_modes) + return NULL; + + for (i = 0; i < connector->count_modes; i++) { + mode = &connector->modes[i]; + if (!strcmp(mode->name, mode_str)) { + /* If the vertical refresh frequency is not specified then return the + * first mode that match with the name. Else, return the mode that match + * the name and the specified vertical refresh frequency. + */ + if (vrefresh == 0) + return mode; + else if (mode->vrefresh == vrefresh) + return mode; + } + } + + return NULL; +} + +static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe) +{ + uint32_t possible_crtcs = ~0; + uint32_t active_crtcs = 0; + unsigned int crtc_idx; + unsigned int i; + int j; + + for (i = 0; i < pipe->num_cons; ++i) { + uint32_t crtcs_for_connector = 0; + drmModeConnector *connector; + drmModeEncoder *encoder; + int idx; + + connector = get_connector_by_id(dev, pipe->con_ids[i]); + if (!connector) + return NULL; + + for (j = 0; j < connector->count_encoders; ++j) { + encoder = get_encoder_by_id(dev, connector->encoders[j]); + if (!encoder) + continue; + + crtcs_for_connector |= encoder->possible_crtcs; + + idx = get_crtc_index(dev, encoder->crtc_id); + if (idx >= 0) + active_crtcs |= 1 << idx; + } + + possible_crtcs &= crtcs_for_connector; + } + + if (!possible_crtcs) + return NULL; + + /* Return the first possible and active CRTC if one exists, or the first + * possible CRTC otherwise. + */ + if (possible_crtcs & active_crtcs) + crtc_idx = ffs(possible_crtcs & active_crtcs); + else + crtc_idx = ffs(possible_crtcs); + + return &dev->resources->crtcs[crtc_idx - 1]; +} + +static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe) +{ + drmModeModeInfo *mode = NULL; + int i; + + pipe->mode = NULL; + + for (i = 0; i < (int)pipe->num_cons; i++) { + mode = connector_find_mode(dev, pipe->con_ids[i], + pipe->mode_str, pipe->vrefresh); + if (mode == NULL) { + fprintf(stderr, + "failed to find mode \"%s\" for connector %u\n", + pipe->mode_str, pipe->con_ids[i]); + return -EINVAL; + } + } + + /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise + * locate a CRTC that can be attached to all the connectors. + */ + if (pipe->crtc_id != (uint32_t)-1) { + for (i = 0; i < dev->resources->res->count_crtcs; i++) { + struct crtc *crtc = &dev->resources->crtcs[i]; + + if (pipe->crtc_id == crtc->crtc->crtc_id) { + pipe->crtc = crtc; + break; + } + } + } else { + pipe->crtc = pipe_find_crtc(dev, pipe); + } + + if (!pipe->crtc) { + fprintf(stderr, "failed to find CRTC for pipe\n"); + return -EINVAL; + } + + pipe->mode = mode; + pipe->crtc->mode = mode; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Properties + */ + +struct property_arg { + uint32_t obj_id; + uint32_t obj_type; + char name[DRM_PROP_NAME_LEN+1]; + uint32_t prop_id; + uint64_t value; +}; + +static void set_property(struct device *dev, struct property_arg *p) +{ + drmModeObjectProperties *props = NULL; + drmModePropertyRes **props_info = NULL; + const char *obj_type; + int ret; + int i; + + p->obj_type = 0; + p->prop_id = 0; + +#define find_object(_res, __res, type, Type) \ + do { \ + for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \ + struct type *obj = &(_res)->type##s[i]; \ + if (obj->type->type##_id != p->obj_id) \ + continue; \ + p->obj_type = DRM_MODE_OBJECT_##Type; \ + obj_type = #Type; \ + props = obj->props; \ + props_info = obj->props_info; \ + } \ + } while(0) \ + + find_object(dev->resources, res, crtc, CRTC); + if (p->obj_type == 0) + find_object(dev->resources, res, connector, CONNECTOR); + if (p->obj_type == 0) + find_object(dev->resources, plane_res, plane, PLANE); + if (p->obj_type == 0) { + fprintf(stderr, "Object %i not found, can't set property\n", + p->obj_id); + return; + } + + if (!props) { + fprintf(stderr, "%s %i has no properties\n", + obj_type, p->obj_id); + return; + } + + for (i = 0; i < (int)props->count_props; ++i) { + if (!props_info[i]) + continue; + if (strcmp(props_info[i]->name, p->name) == 0) + break; + } + + if (i == (int)props->count_props) { + fprintf(stderr, "%s %i has no %s property\n", + obj_type, p->obj_id, p->name); + return; + } + + p->prop_id = props->props[i]; + + ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type, + p->prop_id, p->value); + if (ret < 0) + fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n", + obj_type, p->obj_id, p->name, p->value, strerror(errno)); +} + +/* -------------------------------------------------------------------------- */ + +static void +page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, void *data) +{ + struct pipe_arg *pipe; + unsigned int new_fb_id; + struct timeval end; + double t; + + pipe = data; + if (pipe->current_fb_id == pipe->fb_id[0]) + new_fb_id = pipe->fb_id[1]; + else + new_fb_id = pipe->fb_id[0]; + + drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id, + DRM_MODE_PAGE_FLIP_EVENT, pipe); + pipe->current_fb_id = new_fb_id; + pipe->swap_count++; + if (pipe->swap_count == 60) { + gettimeofday(&end, NULL); + t = end.tv_sec + end.tv_usec * 1e-6 - + (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6); + fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t); + pipe->swap_count = 0; + pipe->start = end; + } +} + +static int set_plane(struct device *dev, struct plane_arg *p) +{ + drmModePlane *ovr; + uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */ + uint32_t plane_id = 0; + struct msm_bo *plane_bo; + uint32_t plane_flags = 0; + int crtc_x, crtc_y, crtc_w, crtc_h; + struct crtc *crtc = NULL; + unsigned int pipe; + unsigned int i; + + /* Find an unused plane which can be connected to our CRTC. Find the + * CRTC index first, then iterate over available planes. + */ + for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) { + if (p->crtc_id == dev->resources->res->crtcs[i]) { + crtc = &dev->resources->crtcs[i]; + pipe = i; + break; + } + } + + if (!crtc) { + fprintf(stderr, "CRTC %u not found\n", p->crtc_id); + return -1; + } + + for (i = 0; i < dev->resources->plane_res->count_planes && !plane_id; i++) { + ovr = dev->resources->planes[i].plane; + if (!ovr) + continue; + + if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id) + plane_id = ovr->plane_id; + } + + if (!plane_id) { + fprintf(stderr, "no unused plane available for CRTC %u\n", + crtc->crtc->crtc_id); + return -1; + } + + fprintf(stderr, "testing %dx%d@%s overlay plane %u\n", + p->w, p->h, p->format_str, plane_id); + + plane_bo = create_test_buffer(dev->msm, p->fourcc, p->w, p->h, handles, + pitches, offsets, PATTERN_TILES); + if (plane_bo == NULL) + return -1; + + /* just use single plane format for now.. */ + if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc, + handles, pitches, offsets, &p->fb_id, plane_flags)) { + fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); + return -1; + } + + crtc_w = p->w * p->scale; + crtc_h = p->h * p->scale; + if (!p->has_position) { + /* Default to the middle of the screen */ + crtc_x = (crtc->mode->hdisplay - crtc_w) / 2; + crtc_y = (crtc->mode->vdisplay - crtc_h) / 2; + } else { + crtc_x = p->x; + crtc_y = p->y; + } + + /* note src coords (last 4 args) are in Q16 format */ + if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id, + plane_flags, crtc_x, crtc_y, crtc_w, crtc_h, + 0, 0, p->w << 16, p->h << 16)) { + fprintf(stderr, "failed to enable plane: %s\n", + strerror(errno)); + return -1; + } + + ovr->crtc_id = crtc->crtc->crtc_id; + + return 0; +} + +static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) +{ + uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */ + unsigned int fb_id; + struct msm_bo *bo; + unsigned int i; + unsigned int j; + int ret, x; + + dev->mode.width = 0; + dev->mode.height = 0; + + for (i = 0; i < count; i++) { + struct pipe_arg *pipe = &pipes[i]; + + ret = pipe_find_crtc_and_mode(dev, pipe); + if (ret < 0) + continue; + + dev->mode.width += pipe->mode->hdisplay; + if (dev->mode.height < pipe->mode->vdisplay) + dev->mode.height = pipe->mode->vdisplay; + } + + bo = create_test_buffer(dev->msm, pipes[0].fourcc, + dev->mode.width, dev->mode.height, + handles, pitches, offsets, PATTERN_SMPTE); + if (bo == NULL) + return; + + ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, + pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0); + if (ret) { + fprintf(stderr, "failed to add fb (%ux%u): %s\n", + dev->mode.width, dev->mode.height, strerror(errno)); + return; + } + + x = 0; + for (i = 0; i < count; i++) { + struct pipe_arg *pipe = &pipes[i]; + + if (pipe->mode == NULL) + continue; + + printf("setting mode %s-%dHz@%s on connectors ", + pipe->mode_str, pipe->mode->vrefresh, pipe->format_str); + for (j = 0; j < pipe->num_cons; ++j) + printf("%u, ", pipe->con_ids[j]); + printf("crtc %d\n", pipe->crtc->crtc->crtc_id); + + ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id, + x, 0, pipe->con_ids, pipe->num_cons, + pipe->mode); + + /* XXX: Actually check if this is needed */ + drmModeDirtyFB(dev->fd, fb_id, NULL, 0); + + x += pipe->mode->hdisplay; + + if (ret) { + fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); + return; + } + } + + dev->mode.bo = bo; + dev->mode.fb_id = fb_id; +} + +static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count) +{ + unsigned int i; + + /* set up planes/overlays */ + for (i = 0; i < count; i++) + if (set_plane(dev, &p[i])) + return; +} + +static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count) +{ + uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */ + unsigned int other_fb_id; + struct msm_bo *other_bo; + drmEventContext evctx; + unsigned int i; + int ret; + + other_bo = create_test_buffer(dev->msm, pipes[0].fourcc, + dev->mode.width, dev->mode.height, + handles, pitches, offsets, PATTERN_PLAIN); + if (other_bo == NULL) + return; + + ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height, + pipes[0].fourcc, handles, pitches, offsets, + &other_fb_id, 0); + if (ret) { + fprintf(stderr, "failed to add fb: %s\n", strerror(errno)); + return; + } + + for (i = 0; i < count; i++) { + struct pipe_arg *pipe = &pipes[i]; + + if (pipe->mode == NULL) + continue; + + ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id, + other_fb_id, DRM_MODE_PAGE_FLIP_EVENT, + pipe); + if (ret) { + fprintf(stderr, "failed to page flip: %s\n", strerror(errno)); + return; + } + gettimeofday(&pipe->start, NULL); + pipe->swap_count = 0; + pipe->fb_id[0] = dev->mode.fb_id; + pipe->fb_id[1] = other_fb_id; + pipe->current_fb_id = other_fb_id; + } + + memset(&evctx, 0, sizeof evctx); + evctx.version = DRM_EVENT_CONTEXT_VERSION; + evctx.vblank_handler = NULL; + evctx.page_flip_handler = page_flip_handler; + + while (1) { +#if 0 + struct pollfd pfd[2]; + + pfd[0].fd = 0; + pfd[0].events = POLLIN; + pfd[1].fd = fd; + pfd[1].events = POLLIN; + + if (poll(pfd, 2, -1) < 0) { + fprintf(stderr, "poll error\n"); + break; + } + + if (pfd[0].revents) + break; +#else + struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; + fd_set fds; + int ret; + + FD_ZERO(&fds); + FD_SET(0, &fds); + FD_SET(dev->fd, &fds); + ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout); + + if (ret <= 0) { + fprintf(stderr, "select timed out or error (ret %d)\n", + ret); + continue; + } else if (FD_ISSET(0, &fds)) { + break; + } +#endif + + drmHandleEvent(dev->fd, &evctx); + } + + msm_bo_destroy(&other_bo); +} + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +static int parse_connector(struct pipe_arg *pipe, const char *arg) +{ + unsigned int len; + unsigned int i; + const char *p; + char *endp; + + pipe->vrefresh = 0; + pipe->crtc_id = (uint32_t)-1; + strcpy(pipe->format_str, "XR24"); + + /* Count the number of connectors and allocate them. */ + pipe->num_cons = 1; + for (p = arg; isdigit(*p) || *p == ','; ++p) { + if (*p == ',') + pipe->num_cons++; + } + + pipe->con_ids = malloc(pipe->num_cons * sizeof *pipe->con_ids); + if (pipe->con_ids == NULL) + return -1; + + /* Parse the connectors. */ + for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) { + pipe->con_ids[i] = strtoul(p, &endp, 10); + if (*endp != ',') + break; + } + + if (i != pipe->num_cons - 1) + return -1; + + /* Parse the remaining parameters. */ + if (*endp == '@') { + arg = endp + 1; + pipe->crtc_id = strtoul(arg, &endp, 10); + } + if (*endp != ':') + return -1; + + arg = endp + 1; + + /* Search for the vertical refresh or the format. */ + p = strpbrk(arg, "-@"); + if (p == NULL) + p = arg + strlen(arg); + len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg)); + strncpy(pipe->mode_str, arg, len); + pipe->mode_str[len] = '\0'; + + if (*p == '-') { + pipe->vrefresh = strtoul(p + 1, &endp, 10); + p = endp; + } + + if (*p == '@') { + strncpy(pipe->format_str, p + 1, 4); + pipe->format_str[4] = '\0'; + } + + pipe->fourcc = format_fourcc(pipe->format_str); + if (pipe->fourcc == 0) { + fprintf(stderr, "unknown format %s\n", pipe->format_str); + return -1; + } + + return 0; +} + +static int parse_plane(struct plane_arg *plane, const char *p) +{ + char *end; + + memset(plane, 0, sizeof *plane); + + plane->crtc_id = strtoul(p, &end, 10); + if (*end != ':') + return -EINVAL; + + p = end + 1; + plane->w = strtoul(p, &end, 10); + if (*end != 'x') + return -EINVAL; + + p = end + 1; + plane->h = strtoul(p, &end, 10); + + if (*end == '+' || *end == '-') { + plane->x = strtol(end, &end, 10); + if (*end != '+' && *end != '-') + return -EINVAL; + plane->y = strtol(end, &end, 10); + + plane->has_position = true; + } + + if (*end == '*') { + p = end + 1; + plane->scale = strtod(p, &end); + if (plane->scale <= 0.0) + return -EINVAL; + } else { + plane->scale = 1.0; + } + + if (*end == '@') { + p = end + 1; + if (strlen(p) != 4) + return -EINVAL; + + strcpy(plane->format_str, p); + } else { + strcpy(plane->format_str, "XR24"); + } + + plane->fourcc = format_fourcc(plane->format_str); + if (plane->fourcc == 0) { + fprintf(stderr, "unknown format %s\n", plane->format_str); + return -EINVAL; + } + + return 0; +} + +static int parse_property(struct property_arg *p, const char *arg) +{ + if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3) + return -1; + + p->obj_type = 0; + p->name[DRM_PROP_NAME_LEN] = '\0'; + + return 0; +} + +static void usage(char *name) +{ + fprintf(stderr, "usage: %s [-cDdefMPpsvw]\n", name); + + fprintf(stderr, "\n Query options:\n\n"); + fprintf(stderr, "\t-c\tlist connectors\n"); + fprintf(stderr, "\t-e\tlist encoders\n"); + fprintf(stderr, "\t-f\tlist framebuffers\n"); + fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n"); + + fprintf(stderr, "\n Test options:\n\n"); + fprintf(stderr, "\t-P <crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n"); + fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n"); + fprintf(stderr, "\t-v\ttest vsynced page flipping\n"); + fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n"); + + fprintf(stderr, "\n Generic options:\n\n"); + fprintf(stderr, "\t-d\tdrop master after mode set\n"); + fprintf(stderr, "\t-M module\tuse the given driver\n"); + fprintf(stderr, "\t-D device\tuse the given device\n"); + + fprintf(stderr, "\n\tDefault is to dump all info.\n"); + exit(0); +} + +static int page_flipping_supported(void) +{ + /*FIXME: generic ioctl needed? */ + return 1; +#if 0 + int ret, value; + struct drm_i915_getparam gp; + + gp.param = I915_PARAM_HAS_PAGEFLIPPING; + gp.value = &value; + + ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp)); + if (ret) { + fprintf(stderr, "drm_i915_getparam: %m\n"); + return 0; + } + + return *gp.value; +#endif +} + +static char optstr[] = "cdD:efM:P:ps:vw:"; + +int main(int argc, char **argv) +{ + struct device dev; + + int c; + int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; + int drop_master = 0; + int test_vsync = 0; + const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tilcdc", "msm" }; + char *device = NULL; + char *module = NULL; + unsigned int i; + int count = 0, plane_count = 0; + unsigned int prop_count = 0; + struct pipe_arg *pipe_args = NULL; + struct plane_arg *plane_args = NULL; + struct property_arg *prop_args = NULL; + unsigned int args = 0; + int ret; + + memset(&dev, 0, sizeof dev); + + opterr = 0; + while ((c = getopt(argc, argv, optstr)) != -1) { + args++; + + switch (c) { + case 'c': + connectors = 1; + break; + case 'D': + device = optarg; + args--; + break; + case 'd': + drop_master = 1; + break; + case 'e': + encoders = 1; + break; + case 'f': + framebuffers = 1; + break; + case 'M': + module = optarg; + /* Preserve the default behaviour of dumping all information. */ + args--; + break; + case 'P': + plane_args = realloc(plane_args, + (plane_count + 1) * sizeof *plane_args); + if (plane_args == NULL) { + fprintf(stderr, "memory allocation failed\n"); + return 1; + } + + if (parse_plane(&plane_args[plane_count], optarg) < 0) + usage(argv[0]); + + plane_count++; + break; + case 'p': + crtcs = 1; + planes = 1; + break; + case 's': + pipe_args = realloc(pipe_args, + (count + 1) * sizeof *pipe_args); + if (pipe_args == NULL) { + fprintf(stderr, "memory allocation failed\n"); + return 1; + } + + if (parse_connector(&pipe_args[count], optarg) < 0) + usage(argv[0]); + + count++; + break; + case 'v': + test_vsync = 1; + break; + case 'w': + prop_args = realloc(prop_args, + (prop_count + 1) * sizeof *prop_args); + if (prop_args == NULL) { + fprintf(stderr, "memory allocation failed\n"); + return 1; + } + + if (parse_property(&prop_args[prop_count], optarg) < 0) + usage(argv[0]); + + prop_count++; + break; + default: + usage(argv[0]); + break; + } + } + + if (!args) + encoders = connectors = crtcs = planes = framebuffers = 1; + + if (module) { + dev.fd = drmOpen(module, device); + if (dev.fd < 0) { + fprintf(stderr, "failed to open device '%s'.\n", module); + return 1; + } + } else { + for (i = 0; i < ARRAY_SIZE(modules); i++) { + printf("trying to open device '%s'...", modules[i]); + dev.fd = drmOpen(modules[i], device); + if (dev.fd < 0) { + printf("failed.\n"); + } else { + printf("success.\n"); + break; + } + } + + if (dev.fd < 0) { + fprintf(stderr, "no device found.\n"); + return 1; + } + } + + if (test_vsync && !page_flipping_supported()) { + fprintf(stderr, "page flipping not supported by drm.\n"); + return -1; + } + + if (test_vsync && !count) { + fprintf(stderr, "page flipping requires at least one -s option.\n"); + return -1; + } + + dev.resources = get_resources(&dev); + if (!dev.resources) { + drmClose(dev.fd); + return 1; + } + +#define dump_resource(dev, res) if (res) dump_##res(dev) + + dump_resource(&dev, encoders); + dump_resource(&dev, connectors); + dump_resource(&dev, crtcs); + dump_resource(&dev, planes); + dump_resource(&dev, framebuffers); + + for (i = 0; i < prop_count; ++i) + set_property(&dev, &prop_args[i]); + + if (count || plane_count) { + ret = msm_create(dev.fd, &dev.msm); + if (ret) { + fprintf(stderr, "failed to create msm driver: %s\n", + strerror(-ret)); + return 1; + } + + if (count) + set_mode(&dev, pipe_args, count); + + if (plane_count) + set_planes(&dev, plane_args, plane_count); + + if (test_vsync) + test_page_flip(&dev, pipe_args, count); + + if (drop_master) + drmDropMaster(dev.fd); + + msm_bo_destroy(&dev.mode.bo); + msm_destroy(&dev.msm); + + getchar(); + } + + free_resources(dev.resources); + + return 0; +} |