This is used by radeon and freedreno and will be used by amdgpu. I looked at switching to libdrm_lists.h, but it's pretty horrible. E.g., DRMLISTFOREACHENTRYSAFE.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- Makefile.sources | 3 +- freedreno/Makefile.sources | 3 +- freedreno/freedreno_priv.h | 2 +- freedreno/list.h | 137 --------------------------------------------- tests/radeon/list.h | 137 --------------------------------------------- tests/radeon/rbo.h | 2 +- util_double_list.h | 137 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 142 insertions(+), 279 deletions(-) delete mode 100644 freedreno/list.h delete mode 100644 tests/radeon/list.h create mode 100644 util_double_list.h
diff --git a/Makefile.sources b/Makefile.sources index f215747..862415c 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -8,7 +8,8 @@ LIBDRM_FILES := \ xf86drmMode.c \ xf86atomic.h \ libdrm.h \ - libdrm_lists.h + libdrm_lists.h \ + util_double_list.h
LIBDRM_H_FILES := \ xf86drm.h \ diff --git a/freedreno/Makefile.sources b/freedreno/Makefile.sources index c8c0244..57a8bf1 100644 --- a/freedreno/Makefile.sources +++ b/freedreno/Makefile.sources @@ -9,8 +9,7 @@ LIBDRM_FREEDRENO_FILES := \ msm/msm_drm.h \ msm/msm_pipe.c \ msm/msm_priv.h \ - msm/msm_ringbuffer.c \ - list.h + msm/msm_ringbuffer.c
LIBDRM_FREEDRENO_KGSL_FILES := \ kgsl/kgsl_bo.c \ diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h index 8e072d9..2a1474d 100644 --- a/freedreno/freedreno_priv.h +++ b/freedreno/freedreno_priv.h @@ -48,7 +48,7 @@ #include "xf86drm.h" #include "xf86atomic.h"
-#include "list.h" +#include "util_double_list.h"
#include "freedreno_drmif.h" #include "freedreno_ringbuffer.h" diff --git a/freedreno/list.h b/freedreno/list.h deleted file mode 100644 index 27e0761..0000000 --- a/freedreno/list.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. 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 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. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - */ - -/** - * \file - * List macros heavily inspired by the Linux kernel - * list handling. No list looping yet. - * - * Is not threadsafe, so common operations need to - * be protected using an external mutex. - */ -#ifndef _U_DOUBLE_LIST_H_ -#define _U_DOUBLE_LIST_H_ - -#include <stddef.h> - -struct list_head -{ - struct list_head *prev; - struct list_head *next; -}; - -static inline void list_inithead(struct list_head *item) -{ - item->prev = item; - item->next = item; -} - -static inline void list_add(struct list_head *item, struct list_head *list) -{ - item->prev = list; - item->next = list->next; - list->next->prev = item; - list->next = item; -} - -static inline void list_addtail(struct list_head *item, struct list_head *list) -{ - item->next = list; - item->prev = list->prev; - list->prev->next = item; - list->prev = item; -} - -static inline void list_replace(struct list_head *from, struct list_head *to) -{ - to->prev = from->prev; - to->next = from->next; - from->next->prev = to; - from->prev->next = to; -} - -static inline void list_del(struct list_head *item) -{ - item->prev->next = item->next; - item->next->prev = item->prev; -} - -static inline void list_delinit(struct list_head *item) -{ - item->prev->next = item->next; - item->next->prev = item->prev; - item->next = item; - item->prev = item; -} - -#define LIST_INITHEAD(__item) list_inithead(__item) -#define LIST_ADD(__item, __list) list_add(__item, __list) -#define LIST_ADDTAIL(__item, __list) list_addtail(__item, __list) -#define LIST_REPLACE(__from, __to) list_replace(__from, __to) -#define LIST_DEL(__item) list_del(__item) -#define LIST_DELINIT(__item) list_delinit(__item) - -#define LIST_ENTRY(__type, __item, __field) \ - ((__type *)(((char *)(__item)) - offsetof(__type, __field))) - -#define LIST_IS_EMPTY(__list) \ - ((__list)->next == (__list)) - -#ifndef container_of -#define container_of(ptr, sample, member) \ - (void *)((char *)(ptr) \ - - ((char *)&(sample)->member - (char *)(sample))) -#endif - -#define LIST_FOR_EACH_ENTRY(pos, head, member) \ - for (pos = container_of((head)->next, pos, member); \ - &pos->member != (head); \ - pos = container_of(pos->member.next, pos, member)) - -#define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \ - for (pos = container_of((head)->next, pos, member), \ - storage = container_of(pos->member.next, pos, member); \ - &pos->member != (head); \ - pos = storage, storage = container_of(storage->member.next, storage, member)) - -#define LIST_FOR_EACH_ENTRY_SAFE_REV(pos, storage, head, member) \ - for (pos = container_of((head)->prev, pos, member), \ - storage = container_of(pos->member.prev, pos, member); \ - &pos->member != (head); \ - pos = storage, storage = container_of(storage->member.prev, storage, member)) - -#define LIST_FOR_EACH_ENTRY_FROM(pos, start, head, member) \ - for (pos = container_of((start), pos, member); \ - &pos->member != (head); \ - pos = container_of(pos->member.next, pos, member)) - -#define LIST_FOR_EACH_ENTRY_FROM_REV(pos, start, head, member) \ - for (pos = container_of((start), pos, member); \ - &pos->member != (head); \ - pos = container_of(pos->member.prev, pos, member)) - -#endif /*_U_DOUBLE_LIST_H_*/ diff --git a/tests/radeon/list.h b/tests/radeon/list.h deleted file mode 100644 index 27e0761..0000000 --- a/tests/radeon/list.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. 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 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. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - */ - -/** - * \file - * List macros heavily inspired by the Linux kernel - * list handling. No list looping yet. - * - * Is not threadsafe, so common operations need to - * be protected using an external mutex. - */ -#ifndef _U_DOUBLE_LIST_H_ -#define _U_DOUBLE_LIST_H_ - -#include <stddef.h> - -struct list_head -{ - struct list_head *prev; - struct list_head *next; -}; - -static inline void list_inithead(struct list_head *item) -{ - item->prev = item; - item->next = item; -} - -static inline void list_add(struct list_head *item, struct list_head *list) -{ - item->prev = list; - item->next = list->next; - list->next->prev = item; - list->next = item; -} - -static inline void list_addtail(struct list_head *item, struct list_head *list) -{ - item->next = list; - item->prev = list->prev; - list->prev->next = item; - list->prev = item; -} - -static inline void list_replace(struct list_head *from, struct list_head *to) -{ - to->prev = from->prev; - to->next = from->next; - from->next->prev = to; - from->prev->next = to; -} - -static inline void list_del(struct list_head *item) -{ - item->prev->next = item->next; - item->next->prev = item->prev; -} - -static inline void list_delinit(struct list_head *item) -{ - item->prev->next = item->next; - item->next->prev = item->prev; - item->next = item; - item->prev = item; -} - -#define LIST_INITHEAD(__item) list_inithead(__item) -#define LIST_ADD(__item, __list) list_add(__item, __list) -#define LIST_ADDTAIL(__item, __list) list_addtail(__item, __list) -#define LIST_REPLACE(__from, __to) list_replace(__from, __to) -#define LIST_DEL(__item) list_del(__item) -#define LIST_DELINIT(__item) list_delinit(__item) - -#define LIST_ENTRY(__type, __item, __field) \ - ((__type *)(((char *)(__item)) - offsetof(__type, __field))) - -#define LIST_IS_EMPTY(__list) \ - ((__list)->next == (__list)) - -#ifndef container_of -#define container_of(ptr, sample, member) \ - (void *)((char *)(ptr) \ - - ((char *)&(sample)->member - (char *)(sample))) -#endif - -#define LIST_FOR_EACH_ENTRY(pos, head, member) \ - for (pos = container_of((head)->next, pos, member); \ - &pos->member != (head); \ - pos = container_of(pos->member.next, pos, member)) - -#define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \ - for (pos = container_of((head)->next, pos, member), \ - storage = container_of(pos->member.next, pos, member); \ - &pos->member != (head); \ - pos = storage, storage = container_of(storage->member.next, storage, member)) - -#define LIST_FOR_EACH_ENTRY_SAFE_REV(pos, storage, head, member) \ - for (pos = container_of((head)->prev, pos, member), \ - storage = container_of(pos->member.prev, pos, member); \ - &pos->member != (head); \ - pos = storage, storage = container_of(storage->member.prev, storage, member)) - -#define LIST_FOR_EACH_ENTRY_FROM(pos, start, head, member) \ - for (pos = container_of((start), pos, member); \ - &pos->member != (head); \ - pos = container_of(pos->member.next, pos, member)) - -#define LIST_FOR_EACH_ENTRY_FROM_REV(pos, start, head, member) \ - for (pos = container_of((start), pos, member); \ - &pos->member != (head); \ - pos = container_of(pos->member.prev, pos, member)) - -#endif /*_U_DOUBLE_LIST_H_*/ diff --git a/tests/radeon/rbo.h b/tests/radeon/rbo.h index c25c73a..9164091 100644 --- a/tests/radeon/rbo.h +++ b/tests/radeon/rbo.h @@ -26,7 +26,7 @@ #ifndef RBO_H #define RBO_H
-#include "list.h" +#include "util_double_list.h"
struct rbo { struct list_head list; diff --git a/util_double_list.h b/util_double_list.h new file mode 100644 index 0000000..27e0761 --- /dev/null +++ b/util_double_list.h @@ -0,0 +1,137 @@ +/* + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ + +/** + * \file + * List macros heavily inspired by the Linux kernel + * list handling. No list looping yet. + * + * Is not threadsafe, so common operations need to + * be protected using an external mutex. + */ +#ifndef _U_DOUBLE_LIST_H_ +#define _U_DOUBLE_LIST_H_ + +#include <stddef.h> + +struct list_head +{ + struct list_head *prev; + struct list_head *next; +}; + +static inline void list_inithead(struct list_head *item) +{ + item->prev = item; + item->next = item; +} + +static inline void list_add(struct list_head *item, struct list_head *list) +{ + item->prev = list; + item->next = list->next; + list->next->prev = item; + list->next = item; +} + +static inline void list_addtail(struct list_head *item, struct list_head *list) +{ + item->next = list; + item->prev = list->prev; + list->prev->next = item; + list->prev = item; +} + +static inline void list_replace(struct list_head *from, struct list_head *to) +{ + to->prev = from->prev; + to->next = from->next; + from->next->prev = to; + from->prev->next = to; +} + +static inline void list_del(struct list_head *item) +{ + item->prev->next = item->next; + item->next->prev = item->prev; +} + +static inline void list_delinit(struct list_head *item) +{ + item->prev->next = item->next; + item->next->prev = item->prev; + item->next = item; + item->prev = item; +} + +#define LIST_INITHEAD(__item) list_inithead(__item) +#define LIST_ADD(__item, __list) list_add(__item, __list) +#define LIST_ADDTAIL(__item, __list) list_addtail(__item, __list) +#define LIST_REPLACE(__from, __to) list_replace(__from, __to) +#define LIST_DEL(__item) list_del(__item) +#define LIST_DELINIT(__item) list_delinit(__item) + +#define LIST_ENTRY(__type, __item, __field) \ + ((__type *)(((char *)(__item)) - offsetof(__type, __field))) + +#define LIST_IS_EMPTY(__list) \ + ((__list)->next == (__list)) + +#ifndef container_of +#define container_of(ptr, sample, member) \ + (void *)((char *)(ptr) \ + - ((char *)&(sample)->member - (char *)(sample))) +#endif + +#define LIST_FOR_EACH_ENTRY(pos, head, member) \ + for (pos = container_of((head)->next, pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.next, pos, member)) + +#define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \ + for (pos = container_of((head)->next, pos, member), \ + storage = container_of(pos->member.next, pos, member); \ + &pos->member != (head); \ + pos = storage, storage = container_of(storage->member.next, storage, member)) + +#define LIST_FOR_EACH_ENTRY_SAFE_REV(pos, storage, head, member) \ + for (pos = container_of((head)->prev, pos, member), \ + storage = container_of(pos->member.prev, pos, member); \ + &pos->member != (head); \ + pos = storage, storage = container_of(storage->member.prev, storage, member)) + +#define LIST_FOR_EACH_ENTRY_FROM(pos, start, head, member) \ + for (pos = container_of((start), pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.next, pos, member)) + +#define LIST_FOR_EACH_ENTRY_FROM_REV(pos, start, head, member) \ + for (pos = container_of((start), pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.prev, pos, member)) + +#endif /*_U_DOUBLE_LIST_H_*/
Used by amdgpu, could be used by other components.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- Makefile.sources | 3 ++- util_math.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 util_math.h
diff --git a/Makefile.sources b/Makefile.sources index 862415c..e1d861b 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -9,7 +9,8 @@ LIBDRM_FILES := \ xf86atomic.h \ libdrm.h \ libdrm_lists.h \ - util_double_list.h + util_double_list.h \ + util_math.h
LIBDRM_H_FILES := \ xf86drm.h \ diff --git a/util_math.h b/util_math.h new file mode 100644 index 0000000..b8de0f8 --- /dev/null +++ b/util_math.h @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _UTIL_MATH_H_ +#define _UTIL_MATH_H_ + +#define MIN2( A, B ) ( (A)<(B) ? (A) : (B) ) +#define MAX2( A, B ) ( (A)>(B) ? (A) : (B) ) + +#define ALIGN( value, alignment ) ( ((value) + (alignment) - 1) & ~((alignment) - 1) ) + +#endif /*_UTIL_MATH_H_*/
This is the new ioctl wrapper used by the new admgpu driver. It's primarily used by xf86-video-amdgpu and mesa.
v2: fix amdgpu_drm.h install v3: Integrate some of the sugestions from Emil: clean up Makefile.am, configure.ac capitalize header guards fix _FILE_OFFSET_BITS with config.h use drm_mmap/drm_munmap Remove unused ARRAY_SIZE macro use shared list implementation use shared math implementation use drmGetNodeTypeFromFd helper
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- Makefile.am | 5 + Makefile.sources | 1 + amdgpu/Makefile.am | 53 ++ amdgpu/amdgpu.h | 1276 ++++++++++++++++++++++++++++++++++++++++++++ amdgpu/amdgpu_bo.c | 626 ++++++++++++++++++++++ amdgpu/amdgpu_cs.c | 981 ++++++++++++++++++++++++++++++++++ amdgpu/amdgpu_device.c | 241 +++++++++ amdgpu/amdgpu_gpu_info.c | 275 ++++++++++ amdgpu/amdgpu_internal.h | 208 ++++++++ amdgpu/amdgpu_vamgr.c | 169 ++++++ amdgpu/libdrm_amdgpu.pc.in | 10 + amdgpu/util_hash.c | 382 +++++++++++++ amdgpu/util_hash.h | 99 ++++ amdgpu/util_hash_table.c | 257 +++++++++ amdgpu/util_hash_table.h | 65 +++ configure.ac | 19 + include/drm/amdgpu_drm.h | 600 +++++++++++++++++++++ 17 files changed, 5267 insertions(+) create mode 100644 amdgpu/Makefile.am create mode 100644 amdgpu/amdgpu.h create mode 100644 amdgpu/amdgpu_bo.c create mode 100644 amdgpu/amdgpu_cs.c create mode 100644 amdgpu/amdgpu_device.c create mode 100644 amdgpu/amdgpu_gpu_info.c create mode 100644 amdgpu/amdgpu_internal.h create mode 100644 amdgpu/amdgpu_vamgr.c create mode 100644 amdgpu/libdrm_amdgpu.pc.in create mode 100644 amdgpu/util_hash.c create mode 100644 amdgpu/util_hash.h create mode 100644 amdgpu/util_hash_table.c create mode 100644 amdgpu/util_hash_table.h create mode 100644 include/drm/amdgpu_drm.h
diff --git a/Makefile.am b/Makefile.am index 42d3d7f..5defeb2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,10 @@ if HAVE_RADEON RADEON_SUBDIR = radeon endif
+if HAVE_AMDGPU +AMDGPU_SUBDIR = amdgpu +endif + if HAVE_OMAP OMAP_SUBDIR = omap endif @@ -79,6 +83,7 @@ SUBDIRS = \ $(INTEL_SUBDIR) \ $(NOUVEAU_SUBDIR) \ $(RADEON_SUBDIR) \ + $(AMDGPU_SUBDIR) \ $(OMAP_SUBDIR) \ $(EXYNOS_SUBDIR) \ $(FREEDRENO_SUBDIR) \ diff --git a/Makefile.sources b/Makefile.sources index e1d861b..09a03a7 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -28,6 +28,7 @@ LIBDRM_INCLUDE_H_FILES := \ include/drm/qxl_drm.h \ include/drm/r128_drm.h \ include/drm/radeon_drm.h \ + include/drm/amdgpu_drm.h \ include/drm/savage_drm.h \ include/drm/sis_drm.h \ include/drm/tegra_drm.h \ diff --git a/amdgpu/Makefile.am b/amdgpu/Makefile.am new file mode 100644 index 0000000..4559467 --- /dev/null +++ b/amdgpu/Makefile.am @@ -0,0 +1,53 @@ +# Copyright © 2008 Jérôme Glisse +# +# 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 (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 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. +# +# Authors: +# Jérôme Glisse glisse@freedesktop.org + +AM_CFLAGS = \ + $(WARN_CFLAGS) -Wno-switch-enum \ + -I$(top_srcdir) \ + $(PTHREADSTUBS_CFLAGS) \ + -I$(top_srcdir)/include/drm + +libdrm_amdgpu_la_LTLIBRARIES = libdrm_amdgpu.la +libdrm_amdgpu_ladir = $(libdir) +libdrm_amdgpu_la_LDFLAGS = -version-number 1:0:0 -no-undefined +libdrm_amdgpu_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@ + +libdrm_amdgpu_la_SOURCES = \ + amdgpu_bo.c \ + amdgpu_cs.c \ + amdgpu_device.c \ + amdgpu_gpu_info.c \ + amdgpu_internal.h \ + amdgpu_vamgr.c \ + util_hash.c \ + util_hash.h \ + util_hash_table.c \ + util_hash_table.h + +libdrm_amdgpuincludedir = ${includedir}/libdrm +libdrm_amdgpuinclude_HEADERS = \ + amdgpu.h + +pkgconfigdir = @pkgconfigdir@ +pkgconfig_DATA = libdrm_amdgpu.pc diff --git a/amdgpu/amdgpu.h b/amdgpu/amdgpu.h new file mode 100644 index 0000000..11a86ef --- /dev/null +++ b/amdgpu/amdgpu.h @@ -0,0 +1,1276 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * +*/ + +/** + * \file amdgpu.h + * + * Declare public libdrm_amdgpu API + * + * This file define API exposed by libdrm_amdgpu library. + * User wanted to use libdrm_amdgpu functionality must include + * this file. + * + */ +#ifndef _AMDGPU_H_ +#define _AMDGPU_H_ + +#include <stdint.h> +#include <stdbool.h> + +struct drm_amdgpu_info_hw_ip; + +/*--------------------------------------------------------------------------*/ +/* --------------------------- Defines ------------------------------------ */ +/*--------------------------------------------------------------------------*/ + +/** + * Define max. number of Command Buffers (IB) which could be sent to the single + * hardware IP to accommodate CE/DE requirements + * + * \sa amdgpu_cs_ib_info +*/ +#define AMDGPU_CS_MAX_IBS_PER_SUBMIT 4 + +/** + * + */ +#define AMDGPU_TIMEOUT_INFINITE 0xffffffffffffffffull + +/** + * The special flag for GFX submission to identify that this is CE IB + * \sa amdgpu_cs_ib_info +*/ +#define AMDGPU_CS_GFX_IB_CE 0x1 + +/** + * The special flag to mark that this IB will re-used + * by client and should not be automatically return back + * to free pool by libdrm_amdgpu when submission is completed. + * + * \sa amdgpu_cs_ib_info +*/ +#define AMDGPU_CS_REUSE_IB 0x2 + +/** + * The special resource flag for IB submission. + * When VRAM is full, some resources may be moved to GTT to make place + * for other resources which want to be in VRAM. This flag affects the order + * in which resources are moved back to VRAM until there is no space there. + * The resources with the highest priority will be moved first. + * The value can be between 0 and 15, inclusive. + */ +#define AMDGPU_IB_RESOURCE_PRIORITY(x) ((x) & 0xf) + + +/*--------------------------------------------------------------------------*/ +/* ----------------------------- Enums ------------------------------------ */ +/*--------------------------------------------------------------------------*/ + +/** + * Enum describing possible handle types + * + * \sa amdgpu_bo_import, amdgpu_bo_export + * +*/ +enum amdgpu_bo_handle_type { + /** GEM flink name (needs DRM authentication, used by DRI2) */ + amdgpu_bo_handle_type_gem_flink_name = 0, + + /** KMS handle which is used by all driver ioctls */ + amdgpu_bo_handle_type_kms = 1, + + /** DMA-buf fd handle */ + amdgpu_bo_handle_type_dma_buf_fd = 2 +}; + +/** + * Enum describing possible context reset states + * + * \sa amdgpu_cs_query_reset_state() + * +*/ +enum amdgpu_cs_ctx_reset_state { + /** No reset was detected */ + amdgpu_cs_reset_no_error = 0, + + /** Reset/TDR was detected and context caused */ + amdgpu_cs_reset_guilty = 1, + + /** Reset/TDR was detected caused by other context */ + amdgpu_cs_reset_innocent = 2, + + /** Reset TDR was detected by cause of it unknown */ + amdgpu_cs_reset_unknown = 3 +}; + +/** + * For performance reasons and to simplify logic libdrm_amdgpu will handle + * IBs only some pre-defined sizes. + * + * \sa amdgpu_cs_alloc_ib() + */ +enum amdgpu_cs_ib_size { + amdgpu_cs_ib_size_4K = 1, + amdgpu_cs_ib_size_16K = 2, + amdgpu_cs_ib_size_32K = 3, + amdgpu_cs_ib_size_64K = 4, + amdgpu_cs_ib_size_128K = 5 +}; + +/** The number of different IB sizes */ +#define AMDGPU_CS_IB_SIZE_NUM 6 + + +/*--------------------------------------------------------------------------*/ +/* -------------------------- Datatypes ----------------------------------- */ +/*--------------------------------------------------------------------------*/ + +/** + * Define opaque pointer to context associated with fd. + * This context will be returned as the result of + * "initialize" function and should be pass as the first + * parameter to any API call + */ +typedef struct amdgpu_device *amdgpu_device_handle; + +/** + * Define GPU Context type as pointer to opaque structure + * Example of GPU Context is the "rendering" context associated + * with OpenGL context (glCreateContext) + */ +typedef struct amdgpu_context *amdgpu_context_handle; + +/** + * Define handle for amdgpu resources: buffer, GDS, etc. + */ +typedef struct amdgpu_bo *amdgpu_bo_handle; + +/** + * Define handle to be used when dealing with command + * buffers (a.k.a. ibs) + * + */ +typedef struct amdgpu_ib *amdgpu_ib_handle; + + +/*--------------------------------------------------------------------------*/ +/* -------------------------- Structures ---------------------------------- */ +/*--------------------------------------------------------------------------*/ + +/** + * Structure describing memory allocation request + * + * \sa amdgpu_bo_alloc() + * +*/ +struct amdgpu_bo_alloc_request { + /** Allocation request. It must be aligned correctly. */ + uint64_t alloc_size; + + /** + * It may be required to have some specific alignment requirements + * for physical back-up storage (e.g. for displayable surface). + * If 0 there is no special alignment requirement + */ + uint64_t phys_alignment; + + /** + * UMD should specify where to allocate memory and how it + * will be accessed by the CPU. + */ + uint32_t preferred_heap; + + /** Additional flags passed on allocation */ + uint64_t flags; +}; + +/** + * Structure describing memory allocation request + * + * \sa amdgpu_bo_alloc() +*/ +struct amdgpu_bo_alloc_result { + /** Assigned virtual MC Base Address */ + uint64_t virtual_mc_base_address; + + /** Handle of allocated memory to be used by the given process only. */ + amdgpu_bo_handle buf_handle; +}; + +/** + * Special UMD specific information associated with buffer. + * + * It may be need to pass some buffer charactersitic as part + * of buffer sharing. Such information are defined UMD and + * opaque for libdrm_amdgpu as well for kernel driver. + * + * \sa amdgpu_bo_set_metadata(), amdgpu_bo_query_info, + * amdgpu_bo_import(), amdgpu_bo_export + * +*/ +struct amdgpu_bo_metadata { + /** Special flag associated with surface */ + uint64_t flags; + + /** + * ASIC-specific tiling information (also used by DCE). + * The encoding is defined by the AMDGPU_TILING_* definitions. + */ + uint64_t tiling_info; + + /** Size of metadata associated with the buffer, in bytes. */ + uint32_t size_metadata; + + /** UMD specific metadata. Opaque for kernel */ + uint32_t umd_metadata[64]; +}; + +/** + * Structure describing allocated buffer. Client may need + * to query such information as part of 'sharing' buffers mechanism + * + * \sa amdgpu_bo_set_metadata(), amdgpu_bo_query_info(), + * amdgpu_bo_import(), amdgpu_bo_export() +*/ +struct amdgpu_bo_info { + /** Allocated memory size */ + uint64_t alloc_size; + + /** + * It may be required to have some specific alignment requirements + * for physical back-up storage. + */ + uint64_t phys_alignment; + + /** + * Assigned virtual MC Base Address. + * \note This information will be returned only if this buffer was + * allocated in the same process otherwise 0 will be returned. + */ + uint64_t virtual_mc_base_address; + + /** Heap where to allocate memory. */ + uint32_t preferred_heap; + + /** Additional allocation flags. */ + uint64_t alloc_flags; + + /** Metadata associated with buffer if any. */ + struct amdgpu_bo_metadata metadata; +}; + +/** + * Structure with information about "imported" buffer + * + * \sa amdgpu_bo_import() + * + */ +struct amdgpu_bo_import_result { + /** Handle of memory/buffer to use */ + amdgpu_bo_handle buf_handle; + + /** Buffer size */ + uint64_t alloc_size; + + /** Assigned virtual MC Base Address */ + uint64_t virtual_mc_base_address; +}; + + +/** + * + * Structure to describe GDS partitioning information. + * \note OA and GWS resources are asscoiated with GDS partition + * + * \sa amdgpu_gpu_resource_query_gds_info + * +*/ +struct amdgpu_gds_resource_info { + uint32_t gds_gfx_partition_size; + uint32_t compute_partition_size; + uint32_t gds_total_size; + uint32_t gws_per_gfx_partition; + uint32_t gws_per_compute_partition; + uint32_t oa_per_gfx_partition; + uint32_t oa_per_compute_partition; +}; + + + +/** + * Structure describing result of request to allocate GDS + * + * \sa amdgpu_gpu_resource_gds_alloc + * +*/ +struct amdgpu_gds_alloc_info { + /** Handle assigned to gds allocation */ + amdgpu_bo_handle resource_handle; + + /** How much was really allocated */ + uint32_t gds_memory_size; + + /** Number of GWS resources allocated */ + uint32_t gws; + + /** Number of OA resources allocated */ + uint32_t oa; +}; + +/** + * Structure to described allocated command buffer (a.k.a. IB) + * + * \sa amdgpu_cs_alloc_ib() + * +*/ +struct amdgpu_cs_ib_alloc_result { + /** IB allocation handle */ + amdgpu_ib_handle handle; + + /** Assigned GPU VM MC Address of command buffer */ + uint64_t mc_address; + + /** Address to be used for CPU access */ + void *cpu; +}; + +/** + * Structure describing IB + * + * \sa amdgpu_cs_request, amdgpu_cs_submit() + * +*/ +struct amdgpu_cs_ib_info { + /** Special flags */ + uint64_t flags; + + /** Handle of command buffer */ + amdgpu_ib_handle ib_handle; + + /** + * Size of Command Buffer to be submitted. + * - The size is in units of dwords (4 bytes). + * - Must be less or equal to the size of allocated IB + * - Could be 0 + */ + uint32_t size; +}; + +/** + * Structure describing submission request + * + * \note We could have several IBs as packet. e.g. CE, CE, DE case for gfx + * + * \sa amdgpu_cs_submit() +*/ +struct amdgpu_cs_request { + /** Specify flags with additional information */ + uint64_t flags; + + /** Specify HW IP block type to which to send the IB. */ + unsigned ip_type; + + /** IP instance index if there are several IPs of the same type. */ + unsigned ip_instance; + + /** + * Specify ring index of the IP. We could have several rings + * in the same IP. E.g. 0 for SDMA0 and 1 for SDMA1. + */ + uint32_t ring; + + /** + * Specify number of resource handles passed. + * Size of 'handles' array + * + */ + uint32_t number_of_resources; + + /** Array of resources used by submission. */ + amdgpu_bo_handle *resources; + + /** Array of resources flags. This is optional and can be NULL. */ + uint8_t *resource_flags; + + /** Number of IBs to submit in the field ibs. */ + uint32_t number_of_ibs; + + /** + * IBs to submit. Those IBs will be submit together as single entity + */ + struct amdgpu_cs_ib_info *ibs; +}; + +/** + * Structure describing request to check submission state using fence + * + * \sa amdgpu_cs_query_fence_status() + * +*/ +struct amdgpu_cs_query_fence { + + /** In which context IB was sent to execution */ + amdgpu_context_handle context; + + /** Timeout in nanoseconds. */ + uint64_t timeout_ns; + + /** To which HW IP type the fence belongs */ + unsigned ip_type; + + /** IP instance index if there are several IPs of the same type. */ + unsigned ip_instance; + + /** Ring index of the HW IP */ + uint32_t ring; + + /** Flags */ + uint64_t flags; + + /** Specify fence for which we need to check + * submission status.*/ + uint64_t fence; +}; + +/** + * Structure which provide information about GPU VM MC Address space + * alignments requirements + * + * \sa amdgpu_query_buffer_size_alignment + */ +struct amdgpu_buffer_size_alignments { + /** Size alignment requirement for allocation in + * local memory */ + uint64_t size_local; + + /** + * Size alignment requirement for allocation in remote memory + */ + uint64_t size_remote; +}; + + +/** + * Structure which provide information about heap + * + * \sa amdgpu_query_heap_info() + * + */ +struct amdgpu_heap_info { + /** Theoretical max. available memory in the given heap */ + uint64_t heap_size; + + /** + * Number of bytes allocated in the heap. This includes all processes + * and private allocations in the kernel. It changes when new buffers + * are allocated, freed, and moved. It cannot be larger than + * heap_size. + */ + uint64_t heap_usage; + + /** + * Theoretical possible max. size of buffer which + * could be allocated in the given heap + */ + uint64_t max_allocation; +}; + + + +/** + * Describe GPU h/w info needed for UMD correct initialization + * + * \sa amdgpu_query_gpu_info() +*/ +struct amdgpu_gpu_info { + /** Asic id */ + uint32_t asic_id; + /**< Chip revision */ + uint32_t chip_rev; + /** Chip external revision */ + uint32_t chip_external_rev; + /** Family ID */ + uint32_t family_id; + /** Special flags */ + uint64_t ids_flags; + /** max engine clock*/ + uint64_t max_engine_clk; + /** number of shader engines */ + uint32_t num_shader_engines; + /** number of shader arrays per engine */ + uint32_t num_shader_arrays_per_engine; + /** Number of available good shader pipes */ + uint32_t avail_quad_shader_pipes; + /** Max. number of shader pipes.(including good and bad pipes */ + uint32_t max_quad_shader_pipes; + /** Number of parameter cache entries per shader quad pipe */ + uint32_t cache_entries_per_quad_pipe; + /** Number of available graphics context */ + uint32_t num_hw_gfx_contexts; + /** Number of render backend pipes */ + uint32_t rb_pipes; + /** Active render backend pipe number */ + uint32_t active_rb_pipes; + /** Enabled render backend pipe mask */ + uint32_t enabled_rb_pipes_mask; + /** Frequency of GPU Counter */ + uint32_t gpu_counter_freq; + /** CC_RB_BACKEND_DISABLE.BACKEND_DISABLE per SE */ + uint32_t backend_disable[4]; + /** Value of MC_ARB_RAMCFG register*/ + uint32_t mc_arb_ramcfg; + /** Value of GB_ADDR_CONFIG */ + uint32_t gb_addr_cfg; + /** Values of the GB_TILE_MODE0..31 registers */ + uint32_t gb_tile_mode[32]; + /** Values of GB_MACROTILE_MODE0..15 registers */ + uint32_t gb_macro_tile_mode[16]; + /** Value of PA_SC_RASTER_CONFIG register per SE */ + uint32_t pa_sc_raster_cfg[4]; + /** Value of PA_SC_RASTER_CONFIG_1 register per SE */ + uint32_t pa_sc_raster_cfg1[4]; + /* CU info */ + uint32_t cu_active_number; + uint32_t cu_ao_mask; + uint32_t cu_bitmap[4][4]; +}; + + +/*--------------------------------------------------------------------------*/ +/*------------------------- Functions --------------------------------------*/ +/*--------------------------------------------------------------------------*/ + +/* + * Initialization / Cleanup + * +*/ + + +/** + * + * \param fd - \c [in] File descriptor for AMD GPU device + * received previously as the result of + * e.g. drmOpen() call. + * For legacy fd type, the DRI2/DRI3 authentication + * should be done before calling this function. + * \param major_version - \c [out] Major version of library. It is assumed + * that adding new functionality will cause + * increase in major version + * \param minor_version - \c [out] Minor version of library + * \param device_handle - \c [out] Pointer to opaque context which should + * be passed as the first parameter on each + * API call + * + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * + * \sa amdgpu_device_deinitialize() +*/ +int amdgpu_device_initialize(int fd, + uint32_t *major_version, + uint32_t *minor_version, + amdgpu_device_handle *device_handle); + + + +/** + * + * When access to such library does not needed any more the special + * function must be call giving opportunity to clean up any + * resources if needed. + * + * \param device_handle - \c [in] Context associated with file + * descriptor for AMD GPU device + * received previously as the + * result e.g. of drmOpen() call. + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_device_initialize() + * +*/ +int amdgpu_device_deinitialize(amdgpu_device_handle device_handle); + + +/* + * Memory Management + * +*/ + +/** + * Allocate memory to be used by UMD for GPU related operations + * + * \param dev - \c [in] Device handle. + * See #amdgpu_device_initialize() + * \param alloc_buffer - \c [in] Pointer to the structure describing an + * allocation request + * \param info - \c [out] Pointer to structure which return + * information about allocated memory + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_bo_free() +*/ +int amdgpu_bo_alloc(amdgpu_device_handle dev, + struct amdgpu_bo_alloc_request *alloc_buffer, + struct amdgpu_bo_alloc_result *info); + +/** + * Associate opaque data with buffer to be queried by another UMD + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param buf_handle - \c [in] Buffer handle + * \param info - \c [in] Metadata to associated with buffer + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code +*/ +int amdgpu_bo_set_metadata(amdgpu_bo_handle buf_handle, + struct amdgpu_bo_metadata *info); + +/** + * Query buffer information including metadata previusly associated with + * buffer. + * + * \param dev - \c [in] Device handle. + * See #amdgpu_device_initialize() + * \param buf_handle - \c [in] Buffer handle + * \param info - \c [out] Structure describing buffer + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_bo_set_metadata(), amdgpu_bo_alloc() +*/ +int amdgpu_bo_query_info(amdgpu_bo_handle buf_handle, + struct amdgpu_bo_info *info); + +/** + * Allow others to get access to buffer + * + * \param dev - \c [in] Device handle. + * See #amdgpu_device_initialize() + * \param buf_handle - \c [in] Buffer handle + * \param type - \c [in] Type of handle requested + * \param shared_handle - \c [out] Special "shared" handle + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_bo_import() + * +*/ +int amdgpu_bo_export(amdgpu_bo_handle buf_handle, + enum amdgpu_bo_handle_type type, + uint32_t *shared_handle); + +/** + * Request access to "shared" buffer + * + * \param dev - \c [in] Device handle. + * See #amdgpu_device_initialize() + * \param type - \c [in] Type of handle requested + * \param shared_handle - \c [in] Shared handle received as result "import" + * operation + * \param output - \c [out] Pointer to structure with information + * about imported buffer + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \note Buffer must be "imported" only using new "fd" (different from + * one used by "exporter"). + * + * \sa amdgpu_bo_export() + * +*/ +int amdgpu_bo_import(amdgpu_device_handle dev, + enum amdgpu_bo_handle_type type, + uint32_t shared_handle, + struct amdgpu_bo_import_result *output); + +/** + * Free previosuly allocated memory + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param buf_handle - \c [in] Buffer handle to free + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \note In the case of memory shared between different applications all + * resources will be “physically” freed only all such applications + * will be terminated + * \note If is UMD responsibility to ‘free’ buffer only when there is no + * more GPU access + * + * \sa amdgpu_bo_set_metadata(), amdgpu_bo_alloc() + * +*/ +int amdgpu_bo_free(amdgpu_bo_handle buf_handle); + +/** + * Request CPU access to GPU accessable memory + * + * \param buf_handle - \c [in] Buffer handle + * \param cpu - \c [out] CPU address to be used for access + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_bo_cpu_unmap() + * +*/ +int amdgpu_bo_cpu_map(amdgpu_bo_handle buf_handle, void **cpu); + +/** + * Release CPU access to GPU memory + * + * \param buf_handle - \c [in] Buffer handle + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_bo_cpu_map() + * +*/ +int amdgpu_bo_cpu_unmap(amdgpu_bo_handle buf_handle); + + +/** + * Wait until a buffer is not used by the device. + * + * \param dev - \c [in] Device handle. See #amdgpu_lib_initialize() + * \param buf_handle - \c [in] Buffer handle. + * \param timeout_ns - Timeout in nanoseconds. + * \param buffer_busy - 0 if buffer is idle, all GPU access was completed + * and no GPU access is scheduled. + * 1 GPU access is in fly or scheduled + * + * \return 0 - on success + * <0 - AMD specific error code + */ +int amdgpu_bo_wait_for_idle(amdgpu_bo_handle buf_handle, + uint64_t timeout_ns, + bool *buffer_busy); + + +/* + * Special GPU Resources + * +*/ + + + +/** + * Query information about GDS + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param gds_info - \c [out] Pointer to structure to get GDS information + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_gpu_resource_query_gds_info(amdgpu_device_handle dev, + struct amdgpu_gds_resource_info * + gds_info); + + +/** + * Allocate GDS partitions + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param gds_size - \c [in] Size of gds allocation. Must be aligned + * accordingly. + * \param alloc_info - \c [out] Pointer to structure to receive information + * about allocation + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * +*/ +int amdgpu_gpu_resource_gds_alloc(amdgpu_device_handle dev, + uint32_t gds_size, + struct amdgpu_gds_alloc_info *alloc_info); + + + + +/** + * Release GDS resource. When GDS and associated resources not needed any + * more UMD should free them + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param handle - \c [in] Handle assigned to GDS allocation + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_gpu_resource_gds_free(amdgpu_bo_handle handle); + + + +/* + * GPU Execution context + * +*/ + +/** + * Create GPU execution Context + * + * For the purpose of GPU Scheduler and GPU Robustness extensions it is + * necessary to have information/identify rendering/compute contexts. + * It also may be needed to associate some specific requirements with such + * contexts. Kernel driver will guarantee that submission from the same + * context will always be executed in order (first come, first serve). + * + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param context - \c [out] GPU Context handle + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_cs_ctx_free() + * +*/ +int amdgpu_cs_ctx_create(amdgpu_device_handle dev, + amdgpu_context_handle *context); + +/** + * + * Destroy GPU execution context when not needed any more + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param context - \c [in] GPU Context handle + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_cs_ctx_create() + * +*/ +int amdgpu_cs_ctx_free(amdgpu_device_handle dev, + amdgpu_context_handle context); + +/** + * Query reset state for the specific GPU Context + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param context - \c [in] GPU Context handle + * \param state - \c [out] Reset state status + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_cs_ctx_create() + * +*/ +int amdgpu_cs_query_reset_state(amdgpu_device_handle dev, + amdgpu_context_handle context, + enum amdgpu_cs_ctx_reset_state *state); + + +/* + * Command Buffers Management + * +*/ + + +/** + * Allocate memory to be filled with PM4 packets and be served as the first + * entry point of execution (a.k.a. Indirect Buffer) + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param context - \c [in] GPU Context which will use IB + * \param ib_size - \c [in] Size of allocation + * \param output - \c [out] Pointer to structure to get information about + * allocated IB + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \sa amdgpu_cs_free_ib() + * +*/ +int amdgpu_cs_alloc_ib(amdgpu_device_handle dev, + amdgpu_context_handle context, + enum amdgpu_cs_ib_size ib_size, + struct amdgpu_cs_ib_alloc_result *output); + +/** + * If UMD has allocates IBs which doesn’t need any more than those IBs must + * be explicitly freed + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param context - \c [in] GPU Context containing IB + * \param handle - \c [in] IB handle + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \note Libdrm_amdgpu will guarantee that it will correctly detect when it + * is safe to return IB to free pool + * + * \sa amdgpu_cs_alloc_ib() + * +*/ +int amdgpu_cs_free_ib(amdgpu_device_handle dev, + amdgpu_context_handle context, + amdgpu_ib_handle handle); + +/** + * Send request to submit command buffers to hardware. + * + * Kernel driver could use GPU Scheduler to make decision when physically + * sent this request to the hardware. Accordingly this request could be put + * in queue and sent for execution later. The only guarantee is that request + * from the same GPU context to the same ip:ip_instance:ring will be executed in + * order. + * + * + * \param dev - \c [in] Device handle. + * See #amdgpu_device_initialize() + * \param context - \c [in] GPU Context + * \param flags - \c [in] Global submission flags + * \param ibs_request - \c [in] Pointer to submission requests. + * We could submit to the several + * engines/rings simulteniously as + * 'atomic' operation + * \param number_of_requests - \c [in] Number of submission requests + * \param fences - \c [out] Pointer to array of data to get + * fences to identify submission + * requests. Timestamps are valid + * in this GPU context and could be used + * to identify/detect completion of + * submission request + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \note It is assumed that by default IB will be returned to free pool + * automatically by libdrm_amdgpu when submission will completed. + * It is possible for UMD to make decision to re-use the same IB in + * this case it should be explicitly freed.\n + * Accordingly, by default, after submission UMD should not touch passed + * IBs. If UMD needs to re-use IB then the special flag AMDGPU_CS_REUSE_IB + * must be passed. + * + * \note It is required to pass correct resource list with buffer handles + * which will be accessible by command buffers from submission + * This will allow kernel driver to correctly implement "paging". + * Failure to do so will have unpredictable results. + * + * \sa amdgpu_command_buffer_alloc(), amdgpu_command_buffer_free(), + * amdgpu_cs_query_fence_status() + * +*/ +int amdgpu_cs_submit(amdgpu_device_handle dev, + amdgpu_context_handle context, + uint64_t flags, + struct amdgpu_cs_request *ibs_request, + uint32_t number_of_requests, + uint64_t *fences); + +/** + * Query status of Command Buffer Submission + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param fence - \c [in] Structure describing fence to query + * \param expired - \c [out] If fence expired or not.\n + * 0 – if fence is not expired\n + * !0 - otherwise + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * + * \note If UMD wants only to check operation status and returned immediately + * then timeout value as 0 must be passed. In this case success will be + * returned in the case if submission was completed or timeout error + * code. + * + * \sa amdgpu_cs_submit() +*/ +int amdgpu_cs_query_fence_status(amdgpu_device_handle dev, + struct amdgpu_cs_query_fence *fence, + uint32_t *expired); + + +/* + * Query / Info API + * +*/ + + +/** + * Query allocation size alignments + * + * UMD should query information about GPU VM MC size alignments requirements + * to be able correctly choose required allocation size and implement + * internal optimization if needed. + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param info - \c [out] Pointer to structure to get size alignment + * requirements + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev, + struct amdgpu_buffer_size_alignments + *info); + + + +/** + * Query firmware versions + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param fw_type - \c [in] AMDGPU_INFO_FW_* + * \param ip_instance - \c [in] Index of the IP block of the same type. + * \param index - \c [in] Index of the engine. (for SDMA and MEC) + * \param version - \c [out] Pointer to to the "version" return value + * \param feature - \c [out] Pointer to to the "feature" return value + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_query_firmware_version(amdgpu_device_handle dev, unsigned fw_type, + unsigned ip_instance, unsigned index, + uint32_t *version, uint32_t *feature); + + + +/** + * Query the number of HW IP instances of a certain type. + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param type - \c [in] Hardware IP block type = AMDGPU_HW_IP_* + * \param count - \c [out] Pointer to structure to get information + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code +*/ +int amdgpu_query_hw_ip_count(amdgpu_device_handle dev, unsigned type, + uint32_t *count); + + + +/** + * Query engine information + * + * This query allows UMD to query information different engines and their + * capabilities. + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param type - \c [in] Hardware IP block type = AMDGPU_HW_IP_* + * \param ip_instance - \c [in] Index of the IP block of the same type. + * \param info - \c [out] Pointer to structure to get information + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code +*/ +int amdgpu_query_hw_ip_info(amdgpu_device_handle dev, unsigned type, + unsigned ip_instance, + struct drm_amdgpu_info_hw_ip *info); + + + + +/** + * Query heap information + * + * This query allows UMD to query potentially available memory resources and + * adjust their logic if necessary. + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param heap - \c [in] Heap type + * \param info - \c [in] Pointer to structure to get needed information + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_query_heap_info(amdgpu_device_handle dev, + uint32_t heap, + uint32_t flags, + struct amdgpu_heap_info *info); + + + +/** + * Get the CRTC ID from the mode object ID + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param id - \c [in] Mode object ID + * \param result - \c [in] Pointer to the CRTC ID + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_query_crtc_from_id(amdgpu_device_handle dev, unsigned id, + int32_t *result); + + + +/** + * Query GPU H/w Info + * + * Query hardware specific information + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param heap - \c [in] Heap type + * \param info - \c [in] Pointer to structure to get needed information + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX Error code + * +*/ +int amdgpu_query_gpu_info(amdgpu_device_handle dev, + struct amdgpu_gpu_info *info); + + + +/** + * Query hardware or driver information. + * + * The return size is query-specific and depends on the "info_id" parameter. + * No more than "size" bytes is returned. + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize() + * \param info_id - \c [in] AMDGPU_INFO_* + * \param size - \c [in] Size of the returned value. + * \param value - \c [out] Pointer to the return value. + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX error code + * +*/ +int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id, + unsigned size, void *value); + + + +/** + * Read a set of consecutive memory-mapped registers. + * Not all registers are allowed to be read by userspace. + * + * \param dev - \c [in] Device handle. See #amdgpu_device_initialize( + * \param dword_offset - \c [in] Register offset in dwords + * \param count - \c [in] The number of registers to read starting + * from the offset + * \param instance - \c [in] GRBM_GFX_INDEX selector. It may have other + * uses. Set it to 0xffffffff if unsure. + * \param flags - \c [in] Flags with additional information. + * \param values - \c [out] The pointer to return values. + * + * \return 0 on success\n + * >0 - AMD specific error code\n + * <0 - Negative POSIX error code + * +*/ +int amdgpu_read_mm_registers(amdgpu_device_handle dev, unsigned dword_offset, + unsigned count, uint32_t instance, uint32_t flags, + uint32_t *values); + + + +/** + * Request GPU access to user allocated memory e.g. via "malloc" + * + * \param dev - [in] Device handle. See #amdgpu_device_initialize() + * \param cpu - [in] CPU address of user allocated memory which we + * want to map to GPU address space (make GPU accessible) + * (This address must be correctly aligned). + * \param size - [in] Size of allocation (must be correctly aligned) + * \param amdgpu_bo_alloc_result - [out] Handle of allocation to be passed as resource + * on submission and be used in other operations.(e.g. for VA submission) + * ( Temporally defined amdgpu_bo_alloc_result as parameter for return mc address. ) + * + * + * \return 0 on success + * >0 - AMD specific error code + * <0 - Negative POSIX Error code + * + * + * \note + * This call doesn't guarantee that such memory will be persistently + * "locked" / make non-pageable. The purpose of this call is to provide + * opportunity for GPU get access to this resource during submission. + * + * The maximum amount of memory which could be mapped in this call depends + * if overcommit is disabled or not. If overcommit is disabled than the max. + * amount of memory to be pinned will be limited by left "free" size in total + * amount of memory which could be locked simultaneously ("GART" size). + * + * Supported (theoretical) max. size of mapping is restricted only by + * "GART" size. + * + * It is responsibility of caller to correctly specify access rights + * on VA assignment. +*/ +int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev, + void *cpu, + uint64_t size, + struct amdgpu_bo_alloc_result *info); + + +#endif /* #ifdef _AMDGPU_H_ */ diff --git a/amdgpu/amdgpu_bo.c b/amdgpu/amdgpu_bo.c new file mode 100644 index 0000000..39641ce --- /dev/null +++ b/amdgpu/amdgpu_bo.c @@ -0,0 +1,626 @@ +/* + * Copyright © 2014 Advanced Micro Devices, Inc. + * 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, 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include "libdrm.h" +#include "xf86drm.h" +#include "amdgpu_drm.h" +#include "amdgpu_internal.h" +#include "util_hash_table.h" + +static void amdgpu_close_kms_handle(amdgpu_device_handle dev, + uint32_t handle) +{ + struct drm_gem_close args = {}; + + args.handle = handle; + drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &args); +} + +void amdgpu_bo_free_internal(amdgpu_bo_handle bo) +{ + /* Remove the buffer from the hash tables. */ + pthread_mutex_lock(&bo->dev->bo_table_mutex); + util_hash_table_remove(bo->dev->bo_handles, + (void*)(uintptr_t)bo->handle); + if (bo->flink_name) { + util_hash_table_remove(bo->dev->bo_flink_names, + (void*)(uintptr_t)bo->flink_name); + } + pthread_mutex_unlock(&bo->dev->bo_table_mutex); + + /* Release CPU access. */ + if (bo->cpu_map_count > 0) { + bo->cpu_map_count = 1; + amdgpu_bo_cpu_unmap(bo); + } + + amdgpu_close_kms_handle(bo->dev, bo->handle); + pthread_mutex_destroy(&bo->cpu_access_mutex); + amdgpu_vamgr_free_va(&bo->dev->vamgr, bo->virtual_mc_base_address, bo->alloc_size); + free(bo); +} + +int amdgpu_bo_alloc(amdgpu_device_handle dev, + struct amdgpu_bo_alloc_request *alloc_buffer, + struct amdgpu_bo_alloc_result *info) +{ + struct amdgpu_bo *bo; + union drm_amdgpu_gem_create args; + unsigned heap = alloc_buffer->preferred_heap; + int r = 0; + + /* It's an error if the heap is not specified */ + if (!(heap & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM))) + return -EINVAL; + + bo = calloc(1, sizeof(struct amdgpu_bo)); + if (!bo) + return -ENOMEM; + + atomic_set(&bo->refcount, 1); + bo->dev = dev; + bo->alloc_size = alloc_buffer->alloc_size; + + memset(&args, 0, sizeof(args)); + args.in.bo_size = alloc_buffer->alloc_size; + args.in.alignment = alloc_buffer->phys_alignment; + + /* Set the placement. */ + args.in.domains = heap & AMDGPU_GEM_DOMAIN_MASK; + args.in.domain_flags = alloc_buffer->flags & AMDGPU_GEM_CREATE_CPU_GTT_MASK; + + /* Allocate the buffer with the preferred heap. */ + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_CREATE, + &args, sizeof(args)); + if (r) { + free(bo); + return r; + } + + bo->handle = args.out.handle; + + pthread_mutex_init(&bo->cpu_access_mutex, NULL); + + /* map the buffer to the GPU virtual address space */ + { + union drm_amdgpu_gem_va va; + + memset(&va, 0, sizeof(va)); + + bo->virtual_mc_base_address = amdgpu_vamgr_find_va(&dev->vamgr, alloc_buffer->alloc_size, alloc_buffer->phys_alignment); + + va.in.handle = bo->handle; + va.in.operation = AMDGPU_VA_OP_MAP; + va.in.flags = AMDGPU_VM_PAGE_READABLE | + AMDGPU_VM_PAGE_WRITEABLE | + AMDGPU_VM_PAGE_EXECUTABLE; + va.in.va_address = bo->virtual_mc_base_address; + + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va)); + if (r || va.out.result == AMDGPU_VA_RESULT_ERROR) { + amdgpu_bo_free_internal(bo); + return r; + } + pthread_mutex_lock(&dev->bo_table_mutex); + + util_hash_table_set(dev->bo_vas, + (void*)(uintptr_t)bo->virtual_mc_base_address, bo); + pthread_mutex_unlock(&dev->bo_table_mutex); + } + + info->buf_handle = bo; + info->virtual_mc_base_address = bo->virtual_mc_base_address; + return 0; +} + +int amdgpu_bo_set_metadata(amdgpu_bo_handle bo, + struct amdgpu_bo_metadata *info) +{ + struct drm_amdgpu_gem_metadata args = {}; + + args.handle = bo->handle; + args.op = AMDGPU_GEM_METADATA_OP_SET_METADATA; + args.data.flags = info->flags; + args.data.tiling_info = info->tiling_info; + + if (info->size_metadata > sizeof(args.data.data)) + return -EINVAL; + + if (info->size_metadata) { + args.data.data_size_bytes = info->size_metadata; + memcpy(args.data.data, info->umd_metadata, info->size_metadata); + } + + return drmCommandWriteRead(bo->dev->fd, + DRM_AMDGPU_GEM_METADATA, + &args, sizeof(args)); +} + +int amdgpu_bo_query_info(amdgpu_bo_handle bo, + struct amdgpu_bo_info *info) +{ + struct drm_amdgpu_gem_metadata metadata = {}; + struct drm_amdgpu_gem_create_in bo_info = {}; + struct drm_amdgpu_gem_op gem_op = {}; + int r; + + /* Query metadata. */ + metadata.handle = bo->handle; + metadata.op = AMDGPU_GEM_METADATA_OP_GET_METADATA; + + r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_METADATA, + &metadata, sizeof(metadata)); + if (r) + return r; + + if (metadata.data.data_size_bytes > + sizeof(info->metadata.umd_metadata)) + return -EINVAL; + + /* Query buffer info. */ + gem_op.handle = bo->handle; + gem_op.op = AMDGPU_GEM_OP_GET_GEM_CREATE_INFO; + gem_op.value = (intptr_t)&bo_info; + + r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_OP, + &gem_op, sizeof(gem_op)); + if (r) + return r; + + memset(info, 0, sizeof(*info)); + info->alloc_size = bo_info.bo_size; + info->phys_alignment = bo_info.alignment; + info->virtual_mc_base_address = bo->virtual_mc_base_address; + info->preferred_heap = bo_info.domains; + info->alloc_flags = bo_info.domain_flags; + info->metadata.flags = metadata.data.flags; + info->metadata.tiling_info = metadata.data.tiling_info; + + info->metadata.size_metadata = metadata.data.data_size_bytes; + if (metadata.data.data_size_bytes > 0) + memcpy(info->metadata.umd_metadata, metadata.data.data, + metadata.data.data_size_bytes); + + return 0; +} + +static void amdgpu_add_handle_to_table(amdgpu_bo_handle bo) +{ + pthread_mutex_lock(&bo->dev->bo_table_mutex); + util_hash_table_set(bo->dev->bo_handles, + (void*)(uintptr_t)bo->handle, bo); + pthread_mutex_unlock(&bo->dev->bo_table_mutex); +} + +static int amdgpu_bo_export_flink(amdgpu_bo_handle bo) +{ + struct drm_gem_flink flink; + int fd, dma_fd; + uint32_t handle; + int r; + + fd = bo->dev->fd; + handle = bo->handle; + if (bo->flink_name) + return 0; + + + if (bo->dev->flink_fd != bo->dev->fd) { + r = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, + &dma_fd); + if (!r) { + r = drmPrimeFDToHandle(bo->dev->flink_fd, dma_fd, &handle); + close(dma_fd); + } + if (r) + return r; + fd = bo->dev->flink_fd; + } + memset(&flink, 0, sizeof(flink)); + flink.handle = handle; + + r = drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink); + if (r) + return r; + + bo->flink_name = flink.name; + + if (bo->dev->flink_fd != bo->dev->fd) { + struct drm_gem_close args = {}; + args.handle = handle; + drmIoctl(bo->dev->flink_fd, DRM_IOCTL_GEM_CLOSE, &args); + } + + pthread_mutex_lock(&bo->dev->bo_table_mutex); + util_hash_table_set(bo->dev->bo_flink_names, + (void*)(uintptr_t)bo->flink_name, + bo); + pthread_mutex_unlock(&bo->dev->bo_table_mutex); + + return 0; +} + +int amdgpu_bo_export(amdgpu_bo_handle bo, + enum amdgpu_bo_handle_type type, + uint32_t *shared_handle) +{ + int r; + + switch (type) { + case amdgpu_bo_handle_type_gem_flink_name: + r = amdgpu_bo_export_flink(bo); + if (r) + return r; + + *shared_handle = bo->flink_name; + return 0; + + case amdgpu_bo_handle_type_kms: + r = amdgpu_bo_export_flink(bo); + if (r) + return r; + + amdgpu_add_handle_to_table(bo); + *shared_handle = bo->handle; + return 0; + + case amdgpu_bo_handle_type_dma_buf_fd: + amdgpu_add_handle_to_table(bo); + return drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, + (int*)shared_handle); + } + return -EINVAL; +} + +int amdgpu_bo_import(amdgpu_device_handle dev, + enum amdgpu_bo_handle_type type, + uint32_t shared_handle, + struct amdgpu_bo_import_result *output) +{ + struct drm_gem_open open_arg = {}; + union drm_amdgpu_gem_va va; + struct amdgpu_bo *bo = NULL; + int r; + int dma_fd; + uint64_t dma_buf_size = 0; + + /* Convert a DMA buf handle to a KMS handle now. */ + if (type == amdgpu_bo_handle_type_dma_buf_fd) { + uint32_t handle; + off_t size; + + /* Get a KMS handle. */ + r = drmPrimeFDToHandle(dev->fd, shared_handle, &handle); + if (r) { + return r; + } + + /* Query the buffer size. */ + size = lseek(shared_handle, 0, SEEK_END); + if (size == (off_t)-1) { + amdgpu_close_kms_handle(dev, handle); + return -errno; + } + lseek(shared_handle, 0, SEEK_SET); + + dma_buf_size = size; + shared_handle = handle; + } + + /* We must maintain a list of pairs <handle, bo>, so that we always + * return the same amdgpu_bo instance for the same handle. */ + pthread_mutex_lock(&dev->bo_table_mutex); + + /* If we have already created a buffer with this handle, find it. */ + switch (type) { + case amdgpu_bo_handle_type_gem_flink_name: + bo = util_hash_table_get(dev->bo_flink_names, + (void*)(uintptr_t)shared_handle); + break; + + case amdgpu_bo_handle_type_dma_buf_fd: + bo = util_hash_table_get(dev->bo_handles, + (void*)(uintptr_t)shared_handle); + break; + + case amdgpu_bo_handle_type_kms: + /* Importing a KMS handle in not allowed. */ + pthread_mutex_unlock(&dev->bo_table_mutex); + return -EPERM; + + default: + pthread_mutex_unlock(&dev->bo_table_mutex); + return -EINVAL; + } + + if (bo) { + pthread_mutex_unlock(&dev->bo_table_mutex); + + /* The buffer already exists, just bump the refcount. */ + atomic_inc(&bo->refcount); + + output->buf_handle = bo; + output->alloc_size = bo->alloc_size; + output->virtual_mc_base_address = + bo->virtual_mc_base_address; + return 0; + } + + bo = calloc(1, sizeof(struct amdgpu_bo)); + if (!bo) { + pthread_mutex_unlock(&dev->bo_table_mutex); + if (type == amdgpu_bo_handle_type_dma_buf_fd) { + amdgpu_close_kms_handle(dev, shared_handle); + } + return -ENOMEM; + } + + /* Open the handle. */ + switch (type) { + case amdgpu_bo_handle_type_gem_flink_name: + open_arg.name = shared_handle; + r = drmIoctl(dev->flink_fd, DRM_IOCTL_GEM_OPEN, &open_arg); + if (r) { + free(bo); + pthread_mutex_unlock(&dev->bo_table_mutex); + return r; + } + + bo->handle = open_arg.handle; + if (dev->flink_fd != dev->fd) { + r = drmPrimeHandleToFD(dev->flink_fd, bo->handle, DRM_CLOEXEC, &dma_fd); + if (r) { + free(bo); + pthread_mutex_unlock(&dev->bo_table_mutex); + return r; + } + r = drmPrimeFDToHandle(dev->fd, dma_fd, &bo->handle ); + + close(dma_fd); + + if (r) { + free(bo); + pthread_mutex_unlock(&dev->bo_table_mutex); + return r; + } + } + bo->flink_name = shared_handle; + bo->alloc_size = open_arg.size; + util_hash_table_set(dev->bo_flink_names, + (void*)(uintptr_t)bo->flink_name, bo); + break; + + case amdgpu_bo_handle_type_dma_buf_fd: + bo->handle = shared_handle; + bo->alloc_size = dma_buf_size; + break; + + case amdgpu_bo_handle_type_kms: + assert(0); /* unreachable */ + } + + /* Initialize it. */ + atomic_set(&bo->refcount, 1); + bo->dev = dev; + pthread_mutex_init(&bo->cpu_access_mutex, NULL); + + bo->virtual_mc_base_address = amdgpu_vamgr_find_va(&dev->vamgr, bo->alloc_size, 1 << 20); + + memset(&va, 0, sizeof(va)); + va.in.handle = bo->handle; + va.in.operation = AMDGPU_VA_OP_MAP; + va.in.va_address = bo->virtual_mc_base_address; + va.in.flags = AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE | + AMDGPU_VM_PAGE_EXECUTABLE; + + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va)); + if (r || va.out.result == AMDGPU_VA_RESULT_ERROR) { + pthread_mutex_unlock(&dev->bo_table_mutex); + amdgpu_vamgr_free_va(&dev->vamgr, bo->virtual_mc_base_address, bo->alloc_size); + amdgpu_bo_reference(&bo, NULL); + return r; + } + + util_hash_table_set(dev->bo_vas, + (void*)(uintptr_t)bo->virtual_mc_base_address, bo); + util_hash_table_set(dev->bo_handles, (void*)(uintptr_t)bo->handle, bo); + pthread_mutex_unlock(&dev->bo_table_mutex); + + output->buf_handle = bo; + output->alloc_size = bo->alloc_size; + output->virtual_mc_base_address = bo->virtual_mc_base_address; + return 0; +} + +int amdgpu_bo_free(amdgpu_bo_handle buf_handle) +{ + /* Just drop the reference. */ + amdgpu_bo_reference(&buf_handle, NULL); + return 0; +} + +int amdgpu_bo_cpu_map(amdgpu_bo_handle bo, void **cpu) +{ + union drm_amdgpu_gem_mmap args; + void *ptr; + int r; + + pthread_mutex_lock(&bo->cpu_access_mutex); + + if (bo->cpu_ptr) { + /* already mapped */ + assert(bo->cpu_map_count > 0); + bo->cpu_map_count++; + *cpu = bo->cpu_ptr; + pthread_mutex_unlock(&bo->cpu_access_mutex); + return 0; + } + + assert(bo->cpu_map_count == 0); + + memset(&args, 0, sizeof(args)); + + /* Query the buffer address (args.addr_ptr). + * The kernel driver ignores the offset and size parameters. */ + args.in.handle = bo->handle; + + r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_MMAP, &args, + sizeof(args)); + if (r) { + pthread_mutex_unlock(&bo->cpu_access_mutex); + return r; + } + + /* Map the buffer. */ + ptr = drm_mmap(NULL, bo->alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED, + bo->dev->fd, args.out.addr_ptr); + if (ptr == MAP_FAILED) { + pthread_mutex_unlock(&bo->cpu_access_mutex); + return -errno; + } + + bo->cpu_ptr = ptr; + bo->cpu_map_count = 1; + pthread_mutex_unlock(&bo->cpu_access_mutex); + + *cpu = ptr; + return 0; +} + +int amdgpu_bo_cpu_unmap(amdgpu_bo_handle bo) +{ + int r; + + pthread_mutex_lock(&bo->cpu_access_mutex); + assert(bo->cpu_map_count >= 0); + + if (bo->cpu_map_count == 0) { + /* not mapped */ + pthread_mutex_unlock(&bo->cpu_access_mutex); + return -EBADMSG; + } + + bo->cpu_map_count--; + if (bo->cpu_map_count > 0) { + /* mapped multiple times */ + pthread_mutex_unlock(&bo->cpu_access_mutex); + return 0; + } + + r = drm_munmap(bo->cpu_ptr, bo->alloc_size) == 0 ? 0 : -errno; + bo->cpu_ptr = NULL; + pthread_mutex_unlock(&bo->cpu_access_mutex); + return r; +} + +int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev, + struct amdgpu_buffer_size_alignments *info) +{ + info->size_local = dev->dev_info.pte_fragment_size; + info->size_remote = dev->dev_info.gart_page_size; + return 0; +} + +int amdgpu_bo_wait_for_idle(amdgpu_bo_handle bo, + uint64_t timeout_ns, + bool *busy) +{ + union drm_amdgpu_gem_wait_idle args; + int r; + + memset(&args, 0, sizeof(args)); + args.in.handle = bo->handle; + args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns); + + r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_WAIT_IDLE, + &args, sizeof(args)); + + if (r == 0) { + *busy = args.out.status; + return 0; + } else { + fprintf(stderr, "amdgpu: GEM_WAIT_IDLE failed with %i\n", r); + return r; + } +} + +int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev, + void *cpu, + uint64_t size, + struct amdgpu_bo_alloc_result *info) +{ + int r; + struct amdgpu_bo *bo; + struct drm_amdgpu_gem_userptr args; + union drm_amdgpu_gem_va va; + + memset(&args, 0, sizeof(args)); + args.addr = (uint64_t)cpu; + args.flags = AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_REGISTER; + args.size = size; + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_USERPTR, + &args, sizeof(args)); + if (r) + return r; + + bo = calloc(1, sizeof(struct amdgpu_bo)); + if (!bo) + return -ENOMEM; + + atomic_set(&bo->refcount, 1); + bo->dev = dev; + bo->alloc_size = size; + bo->handle = args.handle; + bo->virtual_mc_base_address = amdgpu_vamgr_find_va(&dev->vamgr, size, 4 * 1024); + + memset(&va, 0, sizeof(va)); + va.in.handle = bo->handle; + va.in.operation = AMDGPU_VA_OP_MAP; + va.in.flags = AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE | + AMDGPU_VM_PAGE_EXECUTABLE; + va.in.va_address = bo->virtual_mc_base_address; + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va)); + if (r || va.out.result == AMDGPU_VA_RESULT_ERROR) { + amdgpu_bo_free_internal(bo); + return r; + } + util_hash_table_set(dev->bo_vas, + (void*)(uintptr_t)bo->virtual_mc_base_address, bo); + info->buf_handle = bo; + info->virtual_mc_base_address = bo->virtual_mc_base_address; + return r; +} diff --git a/amdgpu/amdgpu_cs.c b/amdgpu/amdgpu_cs.c new file mode 100644 index 0000000..614904d --- /dev/null +++ b/amdgpu/amdgpu_cs.c @@ -0,0 +1,981 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> +#include <sched.h> +#include <sys/ioctl.h> + +#include "xf86drm.h" +#include "amdgpu_drm.h" +#include "amdgpu_internal.h" + +/** + * Create an IB buffer. + * + * \param dev - \c [in] Device handle + * \param context - \c [in] GPU Context + * \param ib_size - \c [in] Size of allocation + * \param ib - \c [out] return the pointer to the created IB buffer + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_create_ib(amdgpu_device_handle dev, + amdgpu_context_handle context, + enum amdgpu_cs_ib_size ib_size, + amdgpu_ib_handle *ib) +{ + struct amdgpu_bo_alloc_request alloc_buffer; + struct amdgpu_bo_alloc_result info; + int r; + void *cpu; + struct amdgpu_ib *new_ib; + + memset(&alloc_buffer, 0, sizeof(alloc_buffer)); + + switch (ib_size) { + case amdgpu_cs_ib_size_4K: + alloc_buffer.alloc_size = 4 * 1024; + break; + case amdgpu_cs_ib_size_16K: + alloc_buffer.alloc_size = 16 * 1024; + break; + case amdgpu_cs_ib_size_32K: + alloc_buffer.alloc_size = 32 * 1024; + break; + case amdgpu_cs_ib_size_64K: + alloc_buffer.alloc_size = 64 * 1024; + break; + case amdgpu_cs_ib_size_128K: + alloc_buffer.alloc_size = 128 * 1024; + break; + default: + return -EINVAL; + } + + alloc_buffer.phys_alignment = 4 * 1024; + + alloc_buffer.preferred_heap = AMDGPU_GEM_DOMAIN_GTT; + + r = amdgpu_bo_alloc(dev, + &alloc_buffer, + &info); + if (r) + return r; + + r = amdgpu_bo_cpu_map(info.buf_handle, &cpu); + if (r) { + amdgpu_bo_free(info.buf_handle); + return r; + } + + new_ib = malloc(sizeof(struct amdgpu_ib)); + if (NULL == new_ib) { + amdgpu_bo_cpu_unmap(info.buf_handle); + amdgpu_bo_free(info.buf_handle); + return -ENOMEM; + } + + new_ib->buf_handle = info.buf_handle; + new_ib->cpu = cpu; + new_ib->virtual_mc_base_address = info.virtual_mc_base_address; + new_ib->ib_size = ib_size; + *ib = new_ib; + return 0; +} + +/** + * Destroy an IB buffer. + * + * \param dev - \c [in] Device handle + * \param ib - \c [in] the IB buffer + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_destroy_ib(amdgpu_device_handle dev, + amdgpu_ib_handle ib) +{ + int r; + r = amdgpu_bo_cpu_unmap(ib->buf_handle); + if (r) + return r; + + r = amdgpu_bo_free(ib->buf_handle); + if (r) + return r; + + free(ib); + return 0; +} + +/** + * Initialize IB pools to empty. + * + * \param context - \c [in] GPU Context + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_init_ib_pool(amdgpu_context_handle context) +{ + int i; + int r; + + r = pthread_mutex_init(&context->pool_mutex, NULL); + if (r) + return r; + + for (i = 0; i < AMDGPU_CS_IB_SIZE_NUM; i++) + LIST_INITHEAD(&context->ib_pools[i]); + + return 0; +} + +/** + * Allocate an IB buffer from IB pools. + * + * \param dev - \c [in] Device handle + * \param context - \c [in] GPU Context + * \param ib_size - \c [in] Size of allocation + * \param ib - \c [out] return the pointer to the allocated IB buffer + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_alloc_from_ib_pool(amdgpu_device_handle dev, + amdgpu_context_handle context, + enum amdgpu_cs_ib_size ib_size, + amdgpu_ib_handle *ib) +{ + int r; + struct list_head *head; + head = &context->ib_pools[ib_size]; + + r = -ENOMEM; + pthread_mutex_lock(&context->pool_mutex); + if (!LIST_IS_EMPTY(head)) { + *ib = LIST_ENTRY(struct amdgpu_ib, head->next, list_node); + LIST_DEL(&(*ib)->list_node); + r = 0; + } + pthread_mutex_unlock(&context->pool_mutex); + + return r; +} + +/** + * Free an IB buffer to IB pools. + * + * \param context - \c [in] GPU Context + * \param ib - \c [in] the IB buffer + * + * \return N/A +*/ +static void amdgpu_cs_free_to_ib_pool(amdgpu_context_handle context, + amdgpu_ib_handle ib) +{ + struct list_head *head; + head = &context->ib_pools[ib->ib_size]; + pthread_mutex_lock(&context->pool_mutex); + LIST_ADD(&ib->list_node, head); + pthread_mutex_unlock(&context->pool_mutex); + return; +} + +/** + * Destroy all IB buffers in pools + * + * \param dev - \c [in] Device handle + * \param context - \c [in] GPU Context + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_destroy_ib_pool(amdgpu_device_handle dev, + amdgpu_context_handle context) +{ + int i; + int r; + struct list_head *head; + struct amdgpu_ib *next; + struct amdgpu_ib *storage; + + r = 0; + pthread_mutex_lock(&context->pool_mutex); + for (i = 0; i < AMDGPU_CS_IB_SIZE_NUM; i++) { + head = &context->ib_pools[i]; + LIST_FOR_EACH_ENTRY_SAFE(next, storage, head, list_node) { + r = amdgpu_cs_destroy_ib(dev, next); + if (r) + break; + } + } + pthread_mutex_unlock(&context->pool_mutex); + pthread_mutex_destroy(&context->pool_mutex); + return r; +} + +/** + * Initialize pending IB lists + * + * \param context - \c [in] GPU Context + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_init_pendings(amdgpu_context_handle context) +{ + unsigned ip, inst; + uint32_t ring; + int r; + + r = pthread_mutex_init(&context->pendings_mutex, NULL); + if (r) + return r; + + for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++) + for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++) + for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++) + LIST_INITHEAD(&context->pendings[ip][inst][ring]); + + LIST_INITHEAD(&context->freed); + return 0; +} + +/** + * Free pending IBs + * + * \param dev - \c [in] Device handle + * \param context - \c [in] GPU Context + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_destroy_pendings(amdgpu_device_handle dev, + amdgpu_context_handle context) +{ + int ip, inst; + uint32_t ring; + int r; + struct amdgpu_ib *next; + struct amdgpu_ib *s; + struct list_head *head; + + r = 0; + pthread_mutex_lock(&context->pendings_mutex); + for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++) + for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++) + for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++) { + head = &context->pendings[ip][inst][ring]; + LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) { + r = amdgpu_cs_destroy_ib(dev, next); + if (r) + break; + } + } + + head = &context->freed; + LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) { + r = amdgpu_cs_destroy_ib(dev, next); + if (r) + break; + } + + pthread_mutex_unlock(&context->pendings_mutex); + pthread_mutex_destroy(&context->pendings_mutex); + return r; +} + +/** + * Add IB to pending IB lists without holding sequence_mutex. + * + * \param context - \c [in] GPU Context + * \param ib - \c [in] ib to added to pending lists + * \param ip - \c [in] hw ip block + * \param ip_instance - \c [in] instance of the hw ip block + * \param ring - \c [in] Ring of hw ip + * + * \return N/A +*/ +static void amdgpu_cs_add_pending(amdgpu_context_handle context, + amdgpu_ib_handle ib, + unsigned ip, unsigned ip_instance, + uint32_t ring) +{ + struct list_head *head; + pthread_mutex_lock(&context->pendings_mutex); + head = &context->pendings[ip][ip_instance][ring]; + LIST_ADDTAIL(&ib->list_node, head); + pthread_mutex_unlock(&context->pendings_mutex); + return; +} + +/** + * Garbage collector on a pending IB list without holding pendings_mutex. + * This function by itself is not multithread safe. + * + * \param context - \c [in] GPU Context + * \param ip - \c [in] hw ip block + * \param ip_instance - \c [in] instance of the hw ip block + * \param ring - \c [in] Ring of hw ip + * \param expired_fence - \c [in] fence expired + * + * \return N/A + * \note Hold pendings_mutex before calling this function. +*/ +static void amdgpu_cs_pending_gc_not_safe(amdgpu_context_handle context, + unsigned ip, unsigned ip_instance, + uint32_t ring, + uint64_t expired_fence) +{ + struct list_head *head; + struct amdgpu_ib *next; + struct amdgpu_ib *s; + int r; + + head = &context->pendings[ip][ip_instance][ring]; + LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) + if (next->cs_handle <= expired_fence) { + LIST_DEL(&next->list_node); + amdgpu_cs_free_to_ib_pool(context, next); + } else { + /* The pending list is a sorted list. + There is no need to continue. */ + break; + } + + /* walk the freed list as well */ + head = &context->freed; + LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) { + bool busy; + + r = amdgpu_bo_wait_for_idle(next->buf_handle, 0, &busy); + if (r || busy) + break; + + LIST_DEL(&next->list_node); + amdgpu_cs_free_to_ib_pool(context, next); + } + + return; +} + +/** + * Garbage collector on a pending IB list + * + * \param context - \c [in] GPU Context + * \param ip - \c [in] hw ip block + * \param ip_instance - \c [in] instance of the hw ip block + * \param ring - \c [in] Ring of hw ip + * \param expired_fence - \c [in] fence expired + * + * \return N/A +*/ +static void amdgpu_cs_pending_gc(amdgpu_context_handle context, + unsigned ip, unsigned ip_instance, + uint32_t ring, + uint64_t expired_fence) +{ + pthread_mutex_lock(&context->pendings_mutex); + amdgpu_cs_pending_gc_not_safe(context, ip, ip_instance, ring, + expired_fence); + pthread_mutex_unlock(&context->pendings_mutex); + return; +} + +/** + * Garbage collector on all pending IB lists + * + * \param context - \c [in] GPU Context + * + * \return N/A +*/ +static void amdgpu_cs_all_pending_gc(amdgpu_context_handle context) +{ + unsigned ip, inst; + uint32_t ring; + uint64_t expired_fences[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS]; + + pthread_mutex_lock(&context->sequence_mutex); + for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++) + for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++) + for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++) + expired_fences[ip][inst][ring] = + context->expired_fences[ip][inst][ring]; + pthread_mutex_unlock(&context->sequence_mutex); + + pthread_mutex_lock(&context->pendings_mutex); + for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++) + for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++) + for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++) + amdgpu_cs_pending_gc_not_safe(context, ip, inst, ring, + expired_fences[ip][inst][ring]); + pthread_mutex_unlock(&context->pendings_mutex); +} + +/** + * Allocate an IB buffer + * If there is no free IB buffer in pools, create one. + * + * \param dev - \c [in] Device handle + * \param context - \c [in] GPU Context + * \param ib_size - \c [in] Size of allocation + * \param ib - \c [out] return the pointer to the allocated IB buffer + * + * \return 0 on success otherwise POSIX Error code +*/ +static int amdgpu_cs_alloc_ib_local(amdgpu_device_handle dev, + amdgpu_context_handle context, + enum amdgpu_cs_ib_size ib_size, + amdgpu_ib_handle *ib) +{ + int r; + + r = amdgpu_cs_alloc_from_ib_pool(dev, context, ib_size, ib); + if (!r) + return r; + + amdgpu_cs_all_pending_gc(context); + + /* Retry to allocate from free IB pools after garbage collector. */ + r = amdgpu_cs_alloc_from_ib_pool(dev, context, ib_size, ib); + if (!r) + return r; + + /* There is no suitable IB in free pools. Create one. */ + r = amdgpu_cs_create_ib(dev, context, ib_size, ib); + return r; +} + +int amdgpu_cs_alloc_ib(amdgpu_device_handle dev, + amdgpu_context_handle context, + enum amdgpu_cs_ib_size ib_size, + struct amdgpu_cs_ib_alloc_result *output) +{ + int r; + amdgpu_ib_handle ib; + + if (NULL == dev) + return -EINVAL; + if (NULL == context) + return -EINVAL; + if (NULL == output) + return -EINVAL; + if (ib_size >= AMDGPU_CS_IB_SIZE_NUM) + return -EINVAL; + + r = amdgpu_cs_alloc_ib_local(dev, context, ib_size, &ib); + if (!r) { + output->handle = ib; + output->cpu = ib->cpu; + output->mc_address = ib->virtual_mc_base_address; + } + + return r; +} + +int amdgpu_cs_free_ib(amdgpu_device_handle dev, + amdgpu_context_handle context, + amdgpu_ib_handle handle) +{ + if (NULL == dev) + return -EINVAL; + if (NULL == context) + return -EINVAL; + if (NULL == handle) + return -EINVAL; + + pthread_mutex_lock(&context->pendings_mutex); + LIST_ADD(&handle->list_node, &context->freed); + pthread_mutex_unlock(&context->pendings_mutex); + return 0; +} + +/** + * Create command submission context + * + * \param dev - \c [in] amdgpu device handle + * \param context - \c [out] amdgpu context handle + * + * \return 0 on success otherwise POSIX Error code +*/ +int amdgpu_cs_ctx_create(amdgpu_device_handle dev, + amdgpu_context_handle *context) +{ + struct amdgpu_context *gpu_context; + union drm_amdgpu_ctx args; + int r; + + if (NULL == dev) + return -EINVAL; + if (NULL == context) + return -EINVAL; + + gpu_context = calloc(1, sizeof(struct amdgpu_context)); + if (NULL == gpu_context) + return -ENOMEM; + + r = pthread_mutex_init(&gpu_context->sequence_mutex, NULL); + if (r) + goto error_mutex; + + r = amdgpu_cs_init_ib_pool(gpu_context); + if (r) + goto error_pool; + + r = amdgpu_cs_init_pendings(gpu_context); + if (r) + goto error_pendings; + + r = amdgpu_cs_alloc_ib_local(dev, gpu_context, amdgpu_cs_ib_size_4K, + &gpu_context->fence_ib); + if (r) + goto error_fence_ib; + + + memset(&args, 0, sizeof(args)); + args.in.op = AMDGPU_CTX_OP_ALLOC_CTX; + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CTX, &args, sizeof(args)); + if (r) + goto error_kernel; + + gpu_context->id = args.out.alloc.ctx_id; + *context = (amdgpu_context_handle)gpu_context; + + return 0; + +error_kernel: + amdgpu_cs_free_ib(dev, gpu_context, gpu_context->fence_ib); + +error_fence_ib: + amdgpu_cs_destroy_pendings(dev, gpu_context); + +error_pendings: + amdgpu_cs_destroy_ib_pool(dev, gpu_context); + +error_pool: + pthread_mutex_destroy(&gpu_context->sequence_mutex); + +error_mutex: + free(gpu_context); + return r; +} + +/** + * Release command submission context + * + * \param dev - \c [in] amdgpu device handle + * \param context - \c [in] amdgpu context handle + * + * \return 0 on success otherwise POSIX Error code +*/ +int amdgpu_cs_ctx_free(amdgpu_device_handle dev, + amdgpu_context_handle context) +{ + int r; + union drm_amdgpu_ctx args; + + if (NULL == dev) + return -EINVAL; + if (NULL == context) + return -EINVAL; + + r = amdgpu_cs_free_ib(dev, context, context->fence_ib); + if (r) + return r; + + r = amdgpu_cs_destroy_pendings(dev, context); + if (r) + return r; + + r = amdgpu_cs_destroy_ib_pool(dev, context); + if (r) + return r; + + pthread_mutex_destroy(&context->sequence_mutex); + + /* now deal with kernel side */ + memset(&args, 0, sizeof(args)); + args.in.op = AMDGPU_CTX_OP_FREE_CTX; + args.in.ctx_id = context->id; + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CTX, &args, sizeof(args)); + + free(context); + + return r; +} + +static int amdgpu_cs_create_bo_list(amdgpu_device_handle dev, + amdgpu_context_handle context, + struct amdgpu_cs_request *request, + amdgpu_ib_handle fence_ib, + uint32_t *handle) +{ + struct drm_amdgpu_bo_list_entry *list; + union drm_amdgpu_bo_list args; + unsigned num_resources; + unsigned i; + int r; + + num_resources = request->number_of_resources; + if (fence_ib) + ++num_resources; + + list = alloca(sizeof(struct drm_amdgpu_bo_list_entry) * num_resources); + + memset(&args, 0, sizeof(args)); + args.in.operation = AMDGPU_BO_LIST_OP_CREATE; + args.in.bo_number = num_resources; + args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry); + args.in.bo_info_ptr = (uint64_t)(uintptr_t)list; + + for (i = 0; i < request->number_of_resources; i++) { + list[i].bo_handle = request->resources[i]->handle; + if (request->resource_flags) + list[i].bo_priority = request->resource_flags[i]; + else + list[i].bo_priority = 0; + } + + if (fence_ib) + list[i].bo_handle = fence_ib->buf_handle->handle; + + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST, + &args, sizeof(args)); + if (r) + return r; + + *handle = args.out.list_handle; + return 0; +} + +static int amdgpu_cs_free_bo_list(amdgpu_device_handle dev, uint32_t handle) +{ + union drm_amdgpu_bo_list args; + int r; + + memset(&args, 0, sizeof(args)); + args.in.operation = AMDGPU_BO_LIST_OP_DESTROY; + args.in.list_handle = handle; + + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST, + &args, sizeof(args)); + + return r; +} + +static uint32_t amdgpu_cs_fence_index(unsigned ip, unsigned ring) +{ + return ip * AMDGPU_CS_MAX_RINGS + ring; +} + +/** + * Submit command to kernel DRM + * \param dev - \c [in] Device handle + * \param context - \c [in] GPU Context + * \param ibs_request - \c [in] Pointer to submission requests + * \param fence - \c [out] return fence for this submission + * + * \return 0 on success otherwise POSIX Error code + * \sa amdgpu_cs_submit() +*/ +static int amdgpu_cs_submit_one(amdgpu_device_handle dev, + amdgpu_context_handle context, + struct amdgpu_cs_request *ibs_request, + uint64_t *fence) +{ + int r; + uint32_t i, size; + union drm_amdgpu_cs cs; + uint64_t *chunk_array; + struct drm_amdgpu_cs_chunk *chunks; + struct drm_amdgpu_cs_chunk_data *chunk_data; + + if (ibs_request->ip_type >= AMDGPU_HW_IP_NUM) + return -EINVAL; + if (ibs_request->ring >= AMDGPU_CS_MAX_RINGS) + return -EINVAL; + if (ibs_request->number_of_ibs > AMDGPU_CS_MAX_IBS_PER_SUBMIT) + return -EINVAL; + + size = (ibs_request->number_of_ibs + 1) * ((sizeof(uint64_t) + + sizeof(struct drm_amdgpu_cs_chunk) + + sizeof(struct drm_amdgpu_cs_chunk_data)) + + ibs_request->number_of_resources + 1) * + sizeof(struct drm_amdgpu_bo_list_entry); + chunk_array = malloc(size); + if (NULL == chunk_array) + return -ENOMEM; + memset(chunk_array, 0, size); + + chunks = (struct drm_amdgpu_cs_chunk *)(chunk_array + ibs_request->number_of_ibs + 1); + chunk_data = (struct drm_amdgpu_cs_chunk_data *)(chunks + ibs_request->number_of_ibs + 1); + + memset(&cs, 0, sizeof(cs)); + cs.in.chunks = (uint64_t)(uintptr_t)chunk_array; + cs.in.ctx_id = context->id; + cs.in.num_chunks = ibs_request->number_of_ibs; + /* IB chunks */ + for (i = 0; i < ibs_request->number_of_ibs; i++) { + struct amdgpu_cs_ib_info *ib; + chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i]; + chunks[i].chunk_id = AMDGPU_CHUNK_ID_IB; + chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_ib) / 4; + chunks[i].chunk_data = (uint64_t)(uintptr_t)&chunk_data[i]; + + ib = &ibs_request->ibs[i]; + + chunk_data[i].ib_data.handle = ib->ib_handle->buf_handle->handle; + chunk_data[i].ib_data.va_start = ib->ib_handle->virtual_mc_base_address; + chunk_data[i].ib_data.ib_bytes = ib->size * 4; + chunk_data[i].ib_data.ip_type = ibs_request->ip_type; + chunk_data[i].ib_data.ip_instance = ibs_request->ip_instance; + chunk_data[i].ib_data.ring = ibs_request->ring; + + if (ib->flags & AMDGPU_CS_GFX_IB_CE) + chunk_data[i].ib_data.flags = AMDGPU_IB_FLAG_CE; + } + + r = amdgpu_cs_create_bo_list(dev, context, ibs_request, NULL, + &cs.in.bo_list_handle); + if (r) + goto error_unlock; + + pthread_mutex_lock(&context->sequence_mutex); + + if (ibs_request->ip_type != AMDGPU_HW_IP_UVD && + ibs_request->ip_type != AMDGPU_HW_IP_VCE) { + i = cs.in.num_chunks++; + + /* fence chunk */ + chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i]; + chunks[i].chunk_id = AMDGPU_CHUNK_ID_FENCE; + chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_fence) / 4; + chunks[i].chunk_data = (uint64_t)(uintptr_t)&chunk_data[i]; + + /* fence bo handle */ + chunk_data[i].fence_data.handle = context->fence_ib->buf_handle->handle; + /* offset */ + chunk_data[i].fence_data.offset = amdgpu_cs_fence_index( + ibs_request->ip_type, ibs_request->ring); + chunk_data[i].fence_data.offset *= sizeof(uint64_t); + } + + r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CS, + &cs, sizeof(cs)); + if (r) + goto error_unlock; + + + /* Hold sequence_mutex while adding record to the pending list. + So the pending list is a sorted list according to fence value. */ + + for (i = 0; i < ibs_request->number_of_ibs; i++) { + struct amdgpu_cs_ib_info *ib; + + ib = &ibs_request->ibs[i]; + if (ib->flags & AMDGPU_CS_REUSE_IB) + continue; + + ib->ib_handle->cs_handle = cs.out.handle; + + amdgpu_cs_add_pending(context, ib->ib_handle, ibs_request->ip_type, + ibs_request->ip_instance, + ibs_request->ring); + } + + *fence = cs.out.handle; + + pthread_mutex_unlock(&context->sequence_mutex); + + r = amdgpu_cs_free_bo_list(dev, cs.in.bo_list_handle); + if (r) + goto error_free; + + free(chunk_array); + return 0; + +error_unlock: + pthread_mutex_unlock(&context->sequence_mutex); + +error_free: + free(chunk_array); + return r; +} + +int amdgpu_cs_submit(amdgpu_device_handle dev, + amdgpu_context_handle context, + uint64_t flags, + struct amdgpu_cs_request *ibs_request, + uint32_t number_of_requests, + uint64_t *fences) +{ + int r; + uint32_t i; + + if (NULL == dev) + return -EINVAL; + if (NULL == context) + return -EINVAL; + if (NULL == ibs_request) + return -EINVAL; + if (NULL == fences) + return -EINVAL; + + r = 0; + for (i = 0; i < number_of_requests; i++) { + r = amdgpu_cs_submit_one(dev, context, ibs_request, fences); + if (r) + break; + fences++; + ibs_request++; + } + + return r; +} + +/** + * Calculate absolute timeout. + * + * \param timeout - \c [in] timeout in nanoseconds. + * + * \return absolute timeout in nanoseconds +*/ +uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout) +{ + int r; + + if (timeout != AMDGPU_TIMEOUT_INFINITE) { + struct timespec current; + r = clock_gettime(CLOCK_MONOTONIC, ¤t); + if (r) + return r; + + timeout += ((uint64_t)current.tv_sec) * 1000000000ull; + timeout += current.tv_nsec; + } + return timeout; +} + +static int amdgpu_ioctl_wait_cs(amdgpu_device_handle dev, + unsigned ip, + unsigned ip_instance, + uint32_t ring, + uint64_t handle, + uint64_t timeout_ns, + bool *busy) +{ + union drm_amdgpu_wait_cs args; + int r; + + memset(&args, 0, sizeof(args)); + args.in.handle = handle; + args.in.ip_type = ip; + args.in.ip_instance = ip_instance; + args.in.ring = ring; + args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns); + + /* Handle errors manually here because of timeout */ + r = ioctl(dev->fd, DRM_IOCTL_AMDGPU_WAIT_CS, &args); + if (r == -1 && (errno == EINTR || errno == EAGAIN)) { + *busy = true; + return 0; + } else if (r) + return -errno; + + *busy = args.out.status; + return 0; +} + +int amdgpu_cs_query_fence_status(amdgpu_device_handle dev, + struct amdgpu_cs_query_fence *fence, + uint32_t *expired) +{ + amdgpu_context_handle context; + uint64_t *signaled_fence; + uint64_t *expired_fence; + unsigned ip_type, ip_instance; + uint32_t ring; + bool busy = true; + int r; + + if (NULL == dev) + return -EINVAL; + if (NULL == fence) + return -EINVAL; + if (NULL == expired) + return -EINVAL; + if (NULL == fence->context) + return -EINVAL; + if (fence->ip_type >= AMDGPU_HW_IP_NUM) + return -EINVAL; + if (fence->ring >= AMDGPU_CS_MAX_RINGS) + return -EINVAL; + + context = fence->context; + ip_type = fence->ip_type; + ip_instance = fence->ip_instance; + ring = fence->ring; + signaled_fence = context->fence_ib->cpu; + signaled_fence += amdgpu_cs_fence_index(ip_type, ring); + expired_fence = &context->expired_fences[ip_type][ip_instance][ring]; + *expired = false; + + pthread_mutex_lock(&context->sequence_mutex); + if (fence->fence <= *expired_fence) { + /* This fence value is expired already. */ + pthread_mutex_unlock(&context->sequence_mutex); + *expired = true; + return 0; + } + + if (fence->fence <= *signaled_fence) { + /* This fence value is signaled already. */ + *expired_fence = *signaled_fence; + pthread_mutex_unlock(&context->sequence_mutex); + amdgpu_cs_pending_gc(context, ip_type, ip_instance, ring, + fence->fence); + *expired = true; + return 0; + } + + pthread_mutex_unlock(&context->sequence_mutex); + + r = amdgpu_ioctl_wait_cs(dev, ip_type, ip_instance, ring, + fence->fence, fence->timeout_ns, &busy); + if (!r && !busy) { + *expired = true; + pthread_mutex_lock(&context->sequence_mutex); + /* The thread doesn't hold sequence_mutex. Other thread could + update *expired_fence already. Check whether there is a + newerly expired fence. */ + if (fence->fence > *expired_fence) { + *expired_fence = fence->fence; + pthread_mutex_unlock(&context->sequence_mutex); + amdgpu_cs_pending_gc(context, ip_type, ip_instance, + ring, fence->fence); + } else { + pthread_mutex_unlock(&context->sequence_mutex); + } + } + + return r; +} + diff --git a/amdgpu/amdgpu_device.c b/amdgpu/amdgpu_device.c new file mode 100644 index 0000000..c610fd3 --- /dev/null +++ b/amdgpu/amdgpu_device.c @@ -0,0 +1,241 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * +*/ + +/** + * \file amdgpu_device.c + * + * Implementation of functions for AMD GPU device + * + * + */ + +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "xf86drm.h" +#include "amdgpu_drm.h" +#include "amdgpu_internal.h" +#include "util_hash_table.h" + +#define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x))) +#define UINT_TO_PTR(x) ((void *)((intptr_t)(x))) +#define RENDERNODE_MINOR_MASK 0xff7f + +pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct util_hash_table *fd_tab; + +static unsigned handle_hash(void *key) +{ + return PTR_TO_UINT(key); +} + +static int handle_compare(void *key1, void *key2) +{ + return PTR_TO_UINT(key1) != PTR_TO_UINT(key2); +} + +static unsigned fd_hash(void *key) +{ + int fd = PTR_TO_UINT(key); + struct stat stat; + fstat(fd, &stat); + + if (!S_ISCHR(stat.st_mode)) + return stat.st_dev ^ stat.st_ino; + else + return stat.st_dev ^ (stat.st_rdev & RENDERNODE_MINOR_MASK); +} + +static int fd_compare(void *key1, void *key2) +{ + int fd1 = PTR_TO_UINT(key1); + int fd2 = PTR_TO_UINT(key2); + struct stat stat1, stat2; + fstat(fd1, &stat1); + fstat(fd2, &stat2); + + if (!S_ISCHR(stat1.st_mode) || !S_ISCHR(stat2.st_mode)) + return stat1.st_dev != stat2.st_dev || + stat1.st_ino != stat2.st_ino; + else + return major(stat1.st_rdev) != major(stat2.st_rdev) || + (minor(stat1.st_rdev) & RENDERNODE_MINOR_MASK) != + (minor(stat2.st_rdev) & RENDERNODE_MINOR_MASK); +} + +/** +* Get the authenticated form fd, +* +* \param fd - \c [in] File descriptor for AMD GPU device +* \param auth - \c [out] Pointer to output the fd is authenticated or not +* A render node fd, output auth = 0 +* A legacy fd, get the authenticated for compatibility root +* +* \return 0 on success\n +* >0 - AMD specific error code\n +* <0 - Negative POSIX Error code +*/ +static int amdgpu_get_auth(int fd, int *auth) +{ + int r = 0; + drm_client_t client; + + if (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER) + *auth = 0; + else { + client.idx = 0; + r = drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client); + if (!r) + *auth = client.auth; + } + return r; +} + +int amdgpu_device_initialize(int fd, + uint32_t *major_version, + uint32_t *minor_version, + amdgpu_device_handle *device_handle) +{ + struct amdgpu_device *dev; + drmVersionPtr version; + int r; + int flag_auth = 0; + int flag_authexist=0; + uint32_t accel_working; + + *device_handle = NULL; + + pthread_mutex_lock(&fd_mutex); + if (!fd_tab) + fd_tab = util_hash_table_create(fd_hash, fd_compare); + r = amdgpu_get_auth(fd, &flag_auth); + if (r) { + pthread_mutex_unlock(&fd_mutex); + return r; + } + dev = util_hash_table_get(fd_tab, UINT_TO_PTR(fd)); + if (dev) { + r = amdgpu_get_auth(dev->fd, &flag_authexist); + if (r) { + pthread_mutex_unlock(&fd_mutex); + return r; + } + if ((flag_auth) && (!flag_authexist)) { + dev->flink_fd = fd; + } + *major_version = dev->major_version; + *minor_version = dev->minor_version; + amdgpu_device_reference(device_handle, dev); + pthread_mutex_unlock(&fd_mutex); + return 0; + } + + dev = calloc(1, sizeof(struct amdgpu_device)); + if (!dev) { + pthread_mutex_unlock(&fd_mutex); + return -ENOMEM; + } + + atomic_set(&dev->refcount, 1); + + version = drmGetVersion(fd); + if (version->version_major != 3) { + fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is " + "only compatible with 3.x.x.\n", + __func__, + version->version_major, + version->version_minor, + version->version_patchlevel); + drmFreeVersion(version); + r = -EBADF; + goto cleanup; + } + + dev->fd = fd; + dev->flink_fd = fd; + dev->major_version = version->version_major; + dev->minor_version = version->version_minor; + drmFreeVersion(version); + + dev->bo_flink_names = util_hash_table_create(handle_hash, + handle_compare); + dev->bo_handles = util_hash_table_create(handle_hash, handle_compare); + dev->bo_vas = util_hash_table_create(handle_hash, handle_compare); + pthread_mutex_init(&dev->bo_table_mutex, NULL); + + /* Check if acceleration is working. */ + r = amdgpu_query_info(dev, AMDGPU_INFO_ACCEL_WORKING, 4, &accel_working); + if (r) + goto cleanup; + if (!accel_working) { + r = -EBADF; + goto cleanup; + } + + r = amdgpu_query_gpu_info_init(dev); + if (r) + goto cleanup; + + amdgpu_vamgr_init(dev); + + *major_version = dev->major_version; + *minor_version = dev->minor_version; + *device_handle = dev; + util_hash_table_set(fd_tab, UINT_TO_PTR(fd), dev); + pthread_mutex_unlock(&fd_mutex); + + return 0; + +cleanup: + free(dev); + pthread_mutex_unlock(&fd_mutex); + return r; +} + +void amdgpu_device_free_internal(amdgpu_device_handle dev) +{ + util_hash_table_destroy(dev->bo_flink_names); + util_hash_table_destroy(dev->bo_handles); + util_hash_table_destroy(dev->bo_vas); + pthread_mutex_destroy(&dev->bo_table_mutex); + pthread_mutex_destroy(&(dev->vamgr.bo_va_mutex)); + util_hash_table_remove(fd_tab, UINT_TO_PTR(dev->fd)); + free(dev); +} + +int amdgpu_device_deinitialize(amdgpu_device_handle dev) +{ + amdgpu_device_reference(&dev, NULL); + return 0; +} + +void amdgpu_device_reference(struct amdgpu_device **dst, + struct amdgpu_device *src) +{ + if (update_references(&(*dst)->refcount, &src->refcount)) + amdgpu_device_free_internal(*dst); + *dst = src; +} diff --git a/amdgpu/amdgpu_gpu_info.c b/amdgpu/amdgpu_gpu_info.c new file mode 100644 index 0000000..0b77731 --- /dev/null +++ b/amdgpu/amdgpu_gpu_info.c @@ -0,0 +1,275 @@ +/* + * Copyright © 2014 Advanced Micro Devices, Inc. + * 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, 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <errno.h> +#include <string.h> + +#include "amdgpu.h" +#include "amdgpu_drm.h" +#include "amdgpu_internal.h" +#include "xf86drm.h" + +int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id, + unsigned size, void *value) +{ + struct drm_amdgpu_info request; + + memset(&request, 0, sizeof(request)); + request.return_pointer = (uintptr_t)value; + request.return_size = size; + request.query = info_id; + + return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request, + sizeof(struct drm_amdgpu_info)); +} + +int amdgpu_query_crtc_from_id(amdgpu_device_handle dev, unsigned id, + int32_t *result) +{ + struct drm_amdgpu_info request; + + memset(&request, 0, sizeof(request)); + request.return_pointer = (uintptr_t)result; + request.return_size = sizeof(*result); + request.query = AMDGPU_INFO_CRTC_FROM_ID; + request.mode_crtc.id = id; + + return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request, + sizeof(struct drm_amdgpu_info)); +} + +int amdgpu_read_mm_registers(amdgpu_device_handle dev, unsigned dword_offset, + unsigned count, uint32_t instance, uint32_t flags, + uint32_t *values) +{ + struct drm_amdgpu_info request; + + memset(&request, 0, sizeof(request)); + request.return_pointer = (uintptr_t)values; + request.return_size = count * sizeof(uint32_t); + request.query = AMDGPU_INFO_READ_MMR_REG; + request.read_mmr_reg.dword_offset = dword_offset; + request.read_mmr_reg.count = count; + request.read_mmr_reg.instance = instance; + request.read_mmr_reg.flags = flags; + + return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request, + sizeof(struct drm_amdgpu_info)); +} + +int amdgpu_query_hw_ip_count(amdgpu_device_handle dev, unsigned type, + uint32_t *count) +{ + struct drm_amdgpu_info request; + + memset(&request, 0, sizeof(request)); + request.return_pointer = (uintptr_t)count; + request.return_size = sizeof(*count); + request.query = AMDGPU_INFO_HW_IP_COUNT; + request.query_hw_ip.type = type; + + return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request, + sizeof(struct drm_amdgpu_info)); +} + +int amdgpu_query_hw_ip_info(amdgpu_device_handle dev, unsigned type, + unsigned ip_instance, + struct drm_amdgpu_info_hw_ip *info) +{ + struct drm_amdgpu_info request; + + memset(&request, 0, sizeof(request)); + request.return_pointer = (uintptr_t)info; + request.return_size = sizeof(*info); + request.query = AMDGPU_INFO_HW_IP_INFO; + request.query_hw_ip.type = type; + request.query_hw_ip.ip_instance = ip_instance; + + return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request, + sizeof(struct drm_amdgpu_info)); +} + +int amdgpu_query_firmware_version(amdgpu_device_handle dev, unsigned fw_type, + unsigned ip_instance, unsigned index, + uint32_t *version, uint32_t *feature) +{ + struct drm_amdgpu_info request; + struct drm_amdgpu_info_firmware firmware; + int r; + + memset(&request, 0, sizeof(request)); + request.return_pointer = (uintptr_t)&firmware; + request.return_size = sizeof(firmware); + request.query = AMDGPU_INFO_FW_VERSION; + request.query_fw.fw_type = fw_type; + request.query_fw.ip_instance = ip_instance; + request.query_fw.index = index; + + r = drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request, + sizeof(struct drm_amdgpu_info)); + if (r) + return r; + + *version = firmware.ver; + *feature = firmware.feature; + return 0; +} + +int amdgpu_query_gpu_info_init(amdgpu_device_handle dev) +{ + int r, i; + + r = amdgpu_query_info(dev, AMDGPU_INFO_DEV_INFO, sizeof(dev->dev_info), + &dev->dev_info); + if (r) + return r; + + dev->info.asic_id = dev->dev_info.device_id; + dev->info.chip_rev = dev->dev_info.chip_rev; + dev->info.chip_external_rev = dev->dev_info.external_rev; + dev->info.family_id = dev->dev_info.family; + dev->info.max_engine_clk = dev->dev_info.max_engine_clock; + dev->info.gpu_counter_freq = dev->dev_info.gpu_counter_freq; + dev->info.enabled_rb_pipes_mask = dev->dev_info.enabled_rb_pipes_mask; + dev->info.rb_pipes = dev->dev_info.num_rb_pipes; + dev->info.ids_flags = dev->dev_info.ids_flags; + dev->info.num_hw_gfx_contexts = dev->dev_info.num_hw_gfx_contexts; + dev->info.num_shader_engines = dev->dev_info.num_shader_engines; + dev->info.num_shader_arrays_per_engine = + dev->dev_info.num_shader_arrays_per_engine; + + for (i = 0; i < (int)dev->info.num_shader_engines; i++) { + unsigned instance = (i << AMDGPU_INFO_MMR_SE_INDEX_SHIFT) | + (AMDGPU_INFO_MMR_SH_INDEX_MASK << + AMDGPU_INFO_MMR_SH_INDEX_SHIFT); + + r = amdgpu_read_mm_registers(dev, 0x263d, 1, instance, 0, + &dev->info.backend_disable[i]); + if (r) + return r; + /* extract bitfield CC_RB_BACKEND_DISABLE.BACKEND_DISABLE */ + dev->info.backend_disable[i] = + (dev->info.backend_disable[i] >> 16) & 0xff; + + r = amdgpu_read_mm_registers(dev, 0xa0d4, 1, instance, 0, + &dev->info.pa_sc_raster_cfg[i]); + if (r) + return r; + + r = amdgpu_read_mm_registers(dev, 0xa0d5, 1, instance, 0, + &dev->info.pa_sc_raster_cfg1[i]); + if (r) + return r; + } + + r = amdgpu_read_mm_registers(dev, 0x2644, 32, 0xffffffff, 0, + dev->info.gb_tile_mode); + if (r) + return r; + + r = amdgpu_read_mm_registers(dev, 0x2664, 16, 0xffffffff, 0, + dev->info.gb_macro_tile_mode); + if (r) + return r; + + r = amdgpu_read_mm_registers(dev, 0x263e, 1, 0xffffffff, 0, + &dev->info.gb_addr_cfg); + if (r) + return r; + + r = amdgpu_read_mm_registers(dev, 0x9d8, 1, 0xffffffff, 0, + &dev->info.mc_arb_ramcfg); + if (r) + return r; + + dev->info.cu_active_number = dev->dev_info.cu_active_number; + dev->info.cu_ao_mask = dev->dev_info.cu_ao_mask; + memcpy(&dev->info.cu_bitmap[0][0], &dev->dev_info.cu_bitmap[0][0], sizeof(dev->info.cu_bitmap)); + + /* TODO: info->max_quad_shader_pipes is not set */ + /* TODO: info->avail_quad_shader_pipes is not set */ + /* TODO: info->cache_entries_per_quad_pipe is not set */ + /* TODO: info->active_rb_pipes is not set */ + return 0; +} + +int amdgpu_query_gpu_info(amdgpu_device_handle dev, + struct amdgpu_gpu_info *info) +{ + /* Get ASIC info*/ + *info = dev->info; + + return 0; +} + +int amdgpu_query_heap_info(amdgpu_device_handle dev, + uint32_t heap, + uint32_t flags, + struct amdgpu_heap_info *info) +{ + struct drm_amdgpu_info_vram_gtt vram_gtt_info; + int r; + + r = amdgpu_query_info(dev, AMDGPU_INFO_VRAM_GTT, + sizeof(vram_gtt_info), &vram_gtt_info); + if (r) + return r; + + /* Get heap information */ + switch (heap) { + case AMDGPU_GEM_DOMAIN_VRAM: + /* query visible only vram heap */ + if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) + info->heap_size = vram_gtt_info.vram_cpu_accessible_size; + else /* query total vram heap */ + info->heap_size = vram_gtt_info.vram_size; + + info->max_allocation = vram_gtt_info.vram_cpu_accessible_size; + + if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) + r = amdgpu_query_info(dev, AMDGPU_INFO_VIS_VRAM_USAGE, + sizeof(info->heap_usage), + &info->heap_usage); + else + r = amdgpu_query_info(dev, AMDGPU_INFO_VRAM_USAGE, + sizeof(info->heap_usage), + &info->heap_usage); + if (r) + return r; + break; + case AMDGPU_GEM_DOMAIN_GTT: + info->heap_size = vram_gtt_info.gtt_size; + info->max_allocation = vram_gtt_info.vram_cpu_accessible_size; + + r = amdgpu_query_info(dev, AMDGPU_INFO_GTT_USAGE, + sizeof(info->heap_usage), + &info->heap_usage); + if (r) + return r; + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/amdgpu/amdgpu_internal.h b/amdgpu/amdgpu_internal.h new file mode 100644 index 0000000..8346f16 --- /dev/null +++ b/amdgpu/amdgpu_internal.h @@ -0,0 +1,208 @@ +/* + * Copyright © 2014 Advanced Micro Devices, Inc. + * 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, 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _AMDGPU_INTERNAL_H_ +#define _AMDGPU_INTERNAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <pthread.h> +#include "xf86atomic.h" +#include "amdgpu.h" +#include "util_double_list.h" + +#define AMDGPU_CS_MAX_RINGS 8 + +struct amdgpu_bo_va_hole { + struct list_head list; + uint64_t offset; + uint64_t size; +}; + +struct amdgpu_bo_va_mgr { + /* the start virtual address */ + uint64_t va_offset; + struct list_head va_holes; + pthread_mutex_t bo_va_mutex; + uint32_t va_alignment; +}; + +struct amdgpu_device { + atomic_t refcount; + int fd; + int flink_fd; + unsigned major_version; + unsigned minor_version; + + /** List of buffer handles. Protected by bo_table_mutex. */ + struct util_hash_table *bo_handles; + /** List of buffer GEM flink names. Protected by bo_table_mutex. */ + struct util_hash_table *bo_flink_names; + /** List of buffer virtual memory ranges. Protected by bo_table_mutex. */ + struct util_hash_table *bo_vas; + /** This protects all hash tables. */ + pthread_mutex_t bo_table_mutex; + struct amdgpu_bo_va_mgr vamgr; + struct drm_amdgpu_info_device dev_info; + struct amdgpu_gpu_info info; +}; + +struct amdgpu_bo { + atomic_t refcount; + struct amdgpu_device *dev; + + uint64_t alloc_size; + uint64_t virtual_mc_base_address; + + uint32_t handle; + uint32_t flink_name; + + pthread_mutex_t cpu_access_mutex; + void *cpu_ptr; + int cpu_map_count; +}; + +/* + * There are three mutexes. + * To avoid deadlock, only hold the mutexes in this order: + * sequence_mutex -> pendings_mutex -> pool_mutex. +*/ +struct amdgpu_context { + /** Mutex for accessing fences and to maintain command submissions + and pending lists in good sequence. */ + pthread_mutex_t sequence_mutex; + /** Buffer for user fences */ + struct amdgpu_ib *fence_ib; + /** The newest expired fence for the ring of the ip blocks. */ + uint64_t expired_fences[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS]; + /** Mutex for accessing pendings list. */ + pthread_mutex_t pendings_mutex; + /** Pending IBs. */ + struct list_head pendings[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS]; + /** Freed IBs not yet in pool */ + struct list_head freed; + /** Mutex for accessing free ib pool. */ + pthread_mutex_t pool_mutex; + /** Internal free IB pools. */ + struct list_head ib_pools[AMDGPU_CS_IB_SIZE_NUM]; + /* context id*/ + uint32_t id; +}; + +struct amdgpu_ib { + struct list_head list_node; + amdgpu_bo_handle buf_handle; + void *cpu; + uint64_t virtual_mc_base_address; + enum amdgpu_cs_ib_size ib_size; + uint64_t cs_handle; +}; + +/** + * Functions. + */ + +void amdgpu_device_free_internal(amdgpu_device_handle dev); + +void amdgpu_bo_free_internal(amdgpu_bo_handle bo); + +void amdgpu_vamgr_init(struct amdgpu_device *dev); + +uint64_t amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, + uint64_t size, uint64_t alignment); + +void amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, + uint64_t size); + +int amdgpu_query_gpu_info_init(amdgpu_device_handle dev); + +uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout); + +/** + * Inline functions. + */ + +/** + * Increment src and decrement dst as if we were updating references + * for an assignment between 2 pointers of some objects. + * + * \return true if dst is 0 + */ +static inline bool update_references(atomic_t *dst, atomic_t *src) +{ + if (dst != src) { + /* bump src first */ + if (src) { + assert(atomic_read(src) > 0); + atomic_inc(src); + } + if (dst) { + assert(atomic_read(dst) > 0); + return atomic_dec_and_test(dst); + } + } + return false; +} + +/** + * Assignment between two amdgpu_bo pointers with reference counting. + * + * Usage: + * struct amdgpu_bo *dst = ... , *src = ...; + * + * dst = src; + * // No reference counting. Only use this when you need to move + * // a reference from one pointer to another. + * + * amdgpu_bo_reference(&dst, src); + * // Reference counters are updated. dst is decremented and src is + * // incremented. dst is freed if its reference counter is 0. + */ +static inline void amdgpu_bo_reference(struct amdgpu_bo **dst, + struct amdgpu_bo *src) +{ + if (update_references(&(*dst)->refcount, &src->refcount)) + amdgpu_bo_free_internal(*dst); + *dst = src; +} + +/** + * Assignment between two amdgpu_device pointers with reference counting. + * + * Usage: + * struct amdgpu_device *dst = ... , *src = ...; + * + * dst = src; + * // No reference counting. Only use this when you need to move + * // a reference from one pointer to another. + * + * amdgpu_device_reference(&dst, src); + * // Reference counters are updated. dst is decremented and src is + * // incremented. dst is freed if its reference counter is 0. + */ +void amdgpu_device_reference(struct amdgpu_device **dst, + struct amdgpu_device *src); +#endif diff --git a/amdgpu/amdgpu_vamgr.c b/amdgpu/amdgpu_vamgr.c new file mode 100644 index 0000000..2335912 --- /dev/null +++ b/amdgpu/amdgpu_vamgr.c @@ -0,0 +1,169 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <stdlib.h> +#include <string.h> +#include "amdgpu.h" +#include "amdgpu_drm.h" +#include "amdgpu_internal.h" +#include "util_math.h" + +void amdgpu_vamgr_init(struct amdgpu_device *dev) +{ + struct amdgpu_bo_va_mgr *vamgr = &dev->vamgr; + + vamgr->va_offset = dev->dev_info.virtual_address_offset; + vamgr->va_alignment = dev->dev_info.virtual_address_alignment; + + list_inithead(&vamgr->va_holes); + pthread_mutex_init(&vamgr->bo_va_mutex, NULL); +} + +uint64_t amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, + uint64_t size, uint64_t alignment) +{ + struct amdgpu_bo_va_hole *hole, *n; + uint64_t offset = 0, waste = 0; + + alignment = MAX2(alignment, mgr->va_alignment); + size = ALIGN(size, mgr->va_alignment); + + pthread_mutex_lock(&mgr->bo_va_mutex); + /* TODO: using more appropriate way to track the holes */ + /* first look for a hole */ + LIST_FOR_EACH_ENTRY_SAFE(hole, n, &mgr->va_holes, list) { + offset = hole->offset; + waste = offset % alignment; + waste = waste ? alignment - waste : 0; + offset += waste; + if (offset >= (hole->offset + hole->size)) { + continue; + } + if (!waste && hole->size == size) { + offset = hole->offset; + list_del(&hole->list); + free(hole); + pthread_mutex_unlock(&mgr->bo_va_mutex); + return offset; + } + if ((hole->size - waste) > size) { + if (waste) { + n = calloc(1, + sizeof(struct amdgpu_bo_va_hole)); + n->size = waste; + n->offset = hole->offset; + list_add(&n->list, &hole->list); + } + hole->size -= (size + waste); + hole->offset += size + waste; + pthread_mutex_unlock(&mgr->bo_va_mutex); + return offset; + } + if ((hole->size - waste) == size) { + hole->size = waste; + pthread_mutex_unlock(&mgr->bo_va_mutex); + return offset; + } + } + + offset = mgr->va_offset; + waste = offset % alignment; + waste = waste ? alignment - waste : 0; + if (waste) { + n = calloc(1, sizeof(struct amdgpu_bo_va_hole)); + n->size = waste; + n->offset = offset; + list_add(&n->list, &mgr->va_holes); + } + offset += waste; + mgr->va_offset += size + waste; + pthread_mutex_unlock(&mgr->bo_va_mutex); + return offset; +} + +void amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, + uint64_t size) +{ + struct amdgpu_bo_va_hole *hole; + + size = ALIGN(size, mgr->va_alignment); + + pthread_mutex_lock(&mgr->bo_va_mutex); + if ((va + size) == mgr->va_offset) { + mgr->va_offset = va; + /* Delete uppermost hole if it reaches the new top */ + if (!LIST_IS_EMPTY(&mgr->va_holes)) { + hole = container_of(mgr->va_holes.next, hole, list); + if ((hole->offset + hole->size) == va) { + mgr->va_offset = hole->offset; + list_del(&hole->list); + free(hole); + } + } + } else { + struct amdgpu_bo_va_hole *next; + + hole = container_of(&mgr->va_holes, hole, list); + LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) { + if (next->offset < va) + break; + hole = next; + } + + if (&hole->list != &mgr->va_holes) { + /* Grow upper hole if it's adjacent */ + if (hole->offset == (va + size)) { + hole->offset = va; + hole->size += size; + /* Merge lower hole if it's adjacent */ + if (next != hole + && &next->list != &mgr->va_holes + && (next->offset + next->size) == va) { + next->size += hole->size; + list_del(&hole->list); + free(hole); + } + goto out; + } + } + + /* Grow lower hole if it's adjacent */ + if (next != hole && &next->list != &mgr->va_holes && + (next->offset + next->size) == va) { + next->size += size; + goto out; + } + + /* FIXME on allocation failure we just lose virtual address space + * maybe print a warning + */ + next = calloc(1, sizeof(struct amdgpu_bo_va_hole)); + if (next) { + next->size = size; + next->offset = va; + list_add(&next->list, &hole->list); + } + } +out: + pthread_mutex_unlock(&mgr->bo_va_mutex); +} diff --git a/amdgpu/libdrm_amdgpu.pc.in b/amdgpu/libdrm_amdgpu.pc.in new file mode 100644 index 0000000..417865e --- /dev/null +++ b/amdgpu/libdrm_amdgpu.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libdrm_amdgpu +Description: Userspace interface to kernel DRM services for amdgpu +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -ldrm_amdgpu +Cflags: -I${includedir} -I${includedir}/libdrm diff --git a/amdgpu/util_hash.c b/amdgpu/util_hash.c new file mode 100644 index 0000000..b1e12c4 --- /dev/null +++ b/amdgpu/util_hash.c @@ -0,0 +1,382 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * 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 VMWARE 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. + * + **************************************************************************/ + + /* + * Authors: + * Zack Rusin zackr@vmware.com + */ + +#include "util_hash.h" + +#include <stdlib.h> +#include <assert.h> + +#define MAX(a, b) ((a > b) ? (a) : (b)) + +static const int MinNumBits = 4; + +static const unsigned char prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +/* Returns the smallest integer n such that + primeForNumBits(n) >= hint. +*/ +static int countBits(int hint) +{ + int numBits = 0; + int bits = hint; + + while (bits > 1) { + bits >>= 1; + numBits++; + } + + if (numBits >= (int)sizeof(prime_deltas)) { + numBits = sizeof(prime_deltas) - 1; + } else if (primeForNumBits(numBits) < hint) { + ++numBits; + } + return numBits; +} + +struct util_node { + struct util_node *next; + unsigned key; + void *value; +}; + +struct util_hash_data { + struct util_node *fakeNext; + struct util_node **buckets; + int size; + int nodeSize; + short userNumBits; + short numBits; + int numBuckets; +}; + +struct util_hash { + union { + struct util_hash_data *d; + struct util_node *e; + } data; +}; + +static void *util_data_allocate_node(struct util_hash_data *hash) +{ + return malloc(hash->nodeSize); +} + +static void util_free_node(struct util_node *node) +{ + free(node); +} + +static struct util_node * +util_hash_create_node(struct util_hash *hash, + unsigned akey, void *avalue, + struct util_node **anextNode) +{ + struct util_node *node = util_data_allocate_node(hash->data.d); + + if (!node) + return NULL; + + node->key = akey; + node->value = avalue; + + node->next = (struct util_node*)(*anextNode); + *anextNode = node; + ++hash->data.d->size; + return node; +} + +static void util_data_rehash(struct util_hash_data *hash, int hint) +{ + if (hint < 0) { + hint = countBits(-hint); + if (hint < MinNumBits) + hint = MinNumBits; + hash->userNumBits = (short)hint; + while (primeForNumBits(hint) < (hash->size >> 1)) + ++hint; + } else if (hint < MinNumBits) { + hint = MinNumBits; + } + + if (hash->numBits != hint) { + struct util_node *e = (struct util_node *)(hash); + struct util_node **oldBuckets = hash->buckets; + int oldNumBuckets = hash->numBuckets; + int i = 0; + + hash->numBits = (short)hint; + hash->numBuckets = primeForNumBits(hint); + hash->buckets = malloc(sizeof(struct util_node*) * hash->numBuckets); + for (i = 0; i < hash->numBuckets; ++i) + hash->buckets[i] = e; + + for (i = 0; i < oldNumBuckets; ++i) { + struct util_node *firstNode = oldBuckets[i]; + while (firstNode != e) { + unsigned h = firstNode->key; + struct util_node *lastNode = firstNode; + struct util_node *afterLastNode; + struct util_node **beforeFirstNode; + + while (lastNode->next != e && lastNode->next->key == h) + lastNode = lastNode->next; + + afterLastNode = lastNode->next; + beforeFirstNode = &hash->buckets[h % hash->numBuckets]; + while (*beforeFirstNode != e) + beforeFirstNode = &(*beforeFirstNode)->next; + lastNode->next = *beforeFirstNode; + *beforeFirstNode = firstNode; + firstNode = afterLastNode; + } + } + free(oldBuckets); + } +} + +static void util_data_might_grow(struct util_hash_data *hash) +{ + if (hash->size >= hash->numBuckets) + util_data_rehash(hash, hash->numBits + 1); +} + +static void util_data_has_shrunk(struct util_hash_data *hash) +{ + if (hash->size <= (hash->numBuckets >> 3) && + hash->numBits > hash->userNumBits) { + int max = MAX(hash->numBits-2, hash->userNumBits); + util_data_rehash(hash, max); + } +} + +static struct util_node *util_data_first_node(struct util_hash_data *hash) +{ + struct util_node *e = (struct util_node *)(hash); + struct util_node **bucket = hash->buckets; + int n = hash->numBuckets; + while (n--) { + if (*bucket != e) + return *bucket; + ++bucket; + } + return e; +} + +static struct util_node **util_hash_find_node(struct util_hash *hash, unsigned akey) +{ + struct util_node **node; + + if (hash->data.d->numBuckets) { + node = (struct util_node **)(&hash->data.d->buckets[akey % hash->data.d->numBuckets]); + assert(*node == hash->data.e || (*node)->next); + while (*node != hash->data.e && (*node)->key != akey) + node = &(*node)->next; + } else { + node = (struct util_node **)((const struct util_node * const *)(&hash->data.e)); + } + return node; +} + +struct util_hash_iter util_hash_insert(struct util_hash *hash, + unsigned key, void *data) +{ + util_data_might_grow(hash->data.d); + + { + struct util_node **nextNode = util_hash_find_node(hash, key); + struct util_node *node = util_hash_create_node(hash, key, data, nextNode); + if (!node) { + struct util_hash_iter null_iter = {hash, 0}; + return null_iter; + } + + { + struct util_hash_iter iter = {hash, node}; + return iter; + } + } +} + +struct util_hash * util_hash_create(void) +{ + struct util_hash *hash = malloc(sizeof(struct util_hash)); + if (!hash) + return NULL; + + hash->data.d = malloc(sizeof(struct util_hash_data)); + if (!hash->data.d) { + free(hash); + return NULL; + } + + hash->data.d->fakeNext = 0; + hash->data.d->buckets = 0; + hash->data.d->size = 0; + hash->data.d->nodeSize = sizeof(struct util_node); + hash->data.d->userNumBits = (short)MinNumBits; + hash->data.d->numBits = 0; + hash->data.d->numBuckets = 0; + + return hash; +} + +void util_hash_delete(struct util_hash *hash) +{ + struct util_node *e_for_x = (struct util_node *)(hash->data.d); + struct util_node **bucket = (struct util_node **)(hash->data.d->buckets); + int n = hash->data.d->numBuckets; + while (n--) { + struct util_node *cur = *bucket++; + while (cur != e_for_x) { + struct util_node *next = cur->next; + util_free_node(cur); + cur = next; + } + } + free(hash->data.d->buckets); + free(hash->data.d); + free(hash); +} + +struct util_hash_iter util_hash_find(struct util_hash *hash, + unsigned key) +{ + struct util_node **nextNode = util_hash_find_node(hash, key); + struct util_hash_iter iter = {hash, *nextNode}; + return iter; +} + +unsigned util_hash_iter_key(struct util_hash_iter iter) +{ + if (!iter.node || iter.hash->data.e == iter.node) + return 0; + return iter.node->key; +} + +void * util_hash_iter_data(struct util_hash_iter iter) +{ + if (!iter.node || iter.hash->data.e == iter.node) + return 0; + return iter.node->value; +} + +static struct util_node *util_hash_data_next(struct util_node *node) +{ + union { + struct util_node *next; + struct util_node *e; + struct util_hash_data *d; + } a; + int start; + struct util_node **bucket; + int n; + + a.next = node->next; + if (!a.next) { + /* iterating beyond the last element */ + return 0; + } + if (a.next->next) + return a.next; + + start = (node->key % a.d->numBuckets) + 1; + bucket = a.d->buckets + start; + n = a.d->numBuckets - start; + while (n--) { + if (*bucket != a.e) + return *bucket; + ++bucket; + } + return a.e; +} + +struct util_hash_iter util_hash_iter_next(struct util_hash_iter iter) +{ + struct util_hash_iter next = {iter.hash, util_hash_data_next(iter.node)}; + return next; +} + +int util_hash_iter_is_null(struct util_hash_iter iter) +{ + if (!iter.node || iter.node == iter.hash->data.e) + return 1; + return 0; +} + +void * util_hash_take(struct util_hash *hash, + unsigned akey) +{ + struct util_node **node = util_hash_find_node(hash, akey); + if (*node != hash->data.e) { + void *t = (*node)->value; + struct util_node *next = (*node)->next; + util_free_node(*node); + *node = next; + --hash->data.d->size; + util_data_has_shrunk(hash->data.d); + return t; + } + return 0; +} + +struct util_hash_iter util_hash_first_node(struct util_hash *hash) +{ + struct util_hash_iter iter = {hash, util_data_first_node(hash->data.d)}; + return iter; +} + +struct util_hash_iter util_hash_erase(struct util_hash *hash, struct util_hash_iter iter) +{ + struct util_hash_iter ret = iter; + struct util_node *node = iter.node; + struct util_node **node_ptr; + + if (node == hash->data.e) + return iter; + + ret = util_hash_iter_next(ret); + node_ptr = (struct util_node**)(&hash->data.d->buckets[node->key % hash->data.d->numBuckets]); + while (*node_ptr != node) + node_ptr = &(*node_ptr)->next; + *node_ptr = node->next; + util_free_node(node); + --hash->data.d->size; + return ret; +} diff --git a/amdgpu/util_hash.h b/amdgpu/util_hash.h new file mode 100644 index 0000000..8e0f9a2 --- /dev/null +++ b/amdgpu/util_hash.h @@ -0,0 +1,99 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * 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 VMWARE 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. + * + **************************************************************************/ + +/** + * @file + * Hash implementation. + * + * This file provides a hash implementation that is capable of dealing + * with collisions. It stores colliding entries in linked list. All + * functions operating on the hash return an iterator. The iterator + * itself points to the collision list. If there wasn't any collision + * the list will have just one entry, otherwise client code should + * iterate over the entries to find the exact entry among ones that + * had the same key (e.g. memcmp could be used on the data to check + * that) + * + * @author Zack Rusin zackr@vmware.com + */ + +#ifndef UTIL_HASH_H +#define UTIL_HASH_H + +#include <stdbool.h> + +struct util_hash; +struct util_node; + +struct util_hash_iter { + struct util_hash *hash; + struct util_node *node; +}; + + +struct util_hash *util_hash_create(void); +void util_hash_delete(struct util_hash *hash); + + +/** + * Adds a data with the given key to the hash. If entry with the given + * key is already in the hash, this current entry is instered before it + * in the collision list. + * Function returns iterator pointing to the inserted item in the hash. + */ +struct util_hash_iter util_hash_insert(struct util_hash *hash, unsigned key, + void *data); + +/** + * Removes the item pointed to by the current iterator from the hash. + * Note that the data itself is not erased and if it was a malloc'ed pointer + * it will have to be freed after calling this function by the callee. + * Function returns iterator pointing to the item after the removed one in + * the hash. + */ +struct util_hash_iter util_hash_erase(struct util_hash *hash, + struct util_hash_iter iter); + +void *util_hash_take(struct util_hash *hash, unsigned key); + + +struct util_hash_iter util_hash_first_node(struct util_hash *hash); + +/** + * Return an iterator pointing to the first entry in the collision list. + */ +struct util_hash_iter util_hash_find(struct util_hash *hash, unsigned key); + + +int util_hash_iter_is_null(struct util_hash_iter iter); +unsigned util_hash_iter_key(struct util_hash_iter iter); +void *util_hash_iter_data(struct util_hash_iter iter); + + +struct util_hash_iter util_hash_iter_next(struct util_hash_iter iter); + +#endif diff --git a/amdgpu/util_hash_table.c b/amdgpu/util_hash_table.c new file mode 100644 index 0000000..cb7213c --- /dev/null +++ b/amdgpu/util_hash_table.c @@ -0,0 +1,257 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * 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 VMWARE 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. + * + **************************************************************************/ + +/** + * @file + * General purpose hash table implementation. + * + * Just uses the util_hash for now, but it might be better switch to a linear + * probing hash table implementation at some point -- as it is said they have + * better lookup and cache performance and it appears to be possible to write + * a lock-free implementation of such hash tables . + * + * @author José Fonseca jfonseca@vmware.com + */ + + + +#include "util_hash_table.h" +#include "util_hash.h" + +#include <stdlib.h> +#include <assert.h> + +struct util_hash_table +{ + struct util_hash *head; + + /** Hash function */ + unsigned (*make_hash)(void *key); + + /** Compare two keys */ + int (*compare)(void *key1, void *key2); +}; + +struct util_hash_table_item +{ + void *key; + void *value; +}; + + +static struct util_hash_table_item * +util_hash_table_item(struct util_hash_iter iter) +{ + return (struct util_hash_table_item *)util_hash_iter_data(iter); +} + +struct util_hash_table *util_hash_table_create(unsigned (*hash)(void *key), + int (*compare)(void *key1, void *key2)) +{ + struct util_hash_table *ht; + + ht = malloc(sizeof(struct util_hash_table)); + if(!ht) + return NULL; + + ht->head = util_hash_create(); + if(!ht->head) { + free(ht); + return NULL; + } + + ht->make_hash = hash; + ht->compare = compare; + + return ht; +} + +static struct util_hash_iter +util_hash_table_find_iter(struct util_hash_table *ht, + void *key, unsigned key_hash) +{ + struct util_hash_iter iter; + struct util_hash_table_item *item; + + iter = util_hash_find(ht->head, key_hash); + while (!util_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)util_hash_iter_data(iter); + if (!ht->compare(item->key, key)) + break; + iter = util_hash_iter_next(iter); + } + + return iter; +} + +static struct util_hash_table_item * +util_hash_table_find_item(struct util_hash_table *ht, + void *key, unsigned key_hash) +{ + struct util_hash_iter iter; + struct util_hash_table_item *item; + + iter = util_hash_find(ht->head, key_hash); + while (!util_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)util_hash_iter_data(iter); + if (!ht->compare(item->key, key)) + return item; + iter = util_hash_iter_next(iter); + } + + return NULL; +} + +void util_hash_table_set(struct util_hash_table *ht, void *key, void *value) +{ + unsigned key_hash; + struct util_hash_table_item *item; + struct util_hash_iter iter; + + assert(ht); + if (!ht) + return; + + key_hash = ht->make_hash(key); + + item = util_hash_table_find_item(ht, key, key_hash); + if(item) { + /* TODO: key/value destruction? */ + item->value = value; + return; + } + + item = malloc(sizeof(struct util_hash_table_item)); + if(!item) + return; + + item->key = key; + item->value = value; + + iter = util_hash_insert(ht->head, key_hash, item); + if(util_hash_iter_is_null(iter)) { + free(item); + return; + } +} + +void *util_hash_table_get(struct util_hash_table *ht, void *key) +{ + unsigned key_hash; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return NULL; + + key_hash = ht->make_hash(key); + + item = util_hash_table_find_item(ht, key, key_hash); + if(!item) + return NULL; + + return item->value; +} + +void util_hash_table_remove(struct util_hash_table *ht, void *key) +{ + unsigned key_hash; + struct util_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + key_hash = ht->make_hash(key); + + iter = util_hash_table_find_iter(ht, key, key_hash); + if(util_hash_iter_is_null(iter)) + return; + + item = util_hash_table_item(iter); + assert(item); + free(item); + + util_hash_erase(ht->head, iter); +} + +void util_hash_table_clear(struct util_hash_table *ht) +{ + struct util_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + iter = util_hash_first_node(ht->head); + while (!util_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)util_hash_take(ht->head, util_hash_iter_key(iter)); + free(item); + iter = util_hash_first_node(ht->head); + } +} + +void util_hash_table_foreach(struct util_hash_table *ht, + void (*callback)(void *key, void *value, void *data), + void *data) +{ + struct util_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + iter = util_hash_first_node(ht->head); + while (!util_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)util_hash_iter_data(iter); + callback(item->key, item->value, data); + iter = util_hash_iter_next(iter); + } +} + +void util_hash_table_destroy(struct util_hash_table *ht) +{ + struct util_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + iter = util_hash_first_node(ht->head); + while (!util_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)util_hash_iter_data(iter); + free(item); + iter = util_hash_iter_next(iter); + } + + util_hash_delete(ht->head); + free(ht); +} diff --git a/amdgpu/util_hash_table.h b/amdgpu/util_hash_table.h new file mode 100644 index 0000000..04fe704 --- /dev/null +++ b/amdgpu/util_hash_table.h @@ -0,0 +1,65 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * 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 VMWARE 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. + * + **************************************************************************/ + +/** + * General purpose hash table. + * + * @author José Fonseca jfonseca@vmware.com + */ + +#ifndef U_HASH_TABLE_H_ +#define U_HASH_TABLE_H_ + +/** + * Generic purpose hash table. + */ +struct util_hash_table; + +/** + * Create an hash table. + * + * @param hash hash function + * @param compare should return 0 for two equal keys. + */ +struct util_hash_table *util_hash_table_create(unsigned (*hash)(void *key), + int (*compare)(void *key1, void *key2)); + +void util_hash_table_set(struct util_hash_table *ht, void *key, void *value); + +void *util_hash_table_get(struct util_hash_table *ht, void *key); + +void util_hash_table_remove(struct util_hash_table *ht, void *key); + +void util_hash_table_clear(struct util_hash_table *ht); + +void util_hash_table_foreach(struct util_hash_table *ht, + void (*callback)(void *key, void *value, void *data), + void *data); + +void util_hash_table_destroy(struct util_hash_table *ht); + +#endif /* U_HASH_TABLE_H_ */ diff --git a/configure.ac b/configure.ac index 155d577..a8997c6 100644 --- a/configure.ac +++ b/configure.ac @@ -74,6 +74,11 @@ AC_ARG_ENABLE(radeon, [Enable support for radeon's KMS API (default: auto)]), [RADEON=$enableval], [RADEON=auto])
+AC_ARG_ENABLE(amdgpu, + AS_HELP_STRING([--disable-amdgpu], + [Enable support for amdgpu's KMS API (default: auto)]), + [AMDGPU=$enableval], [AMDGPU=auto]) + AC_ARG_ENABLE(nouveau, AS_HELP_STRING([--disable-nouveau], [Enable support for nouveau's KMS API (default: auto)]), @@ -236,6 +241,9 @@ if test "x$drm_cv_atomic_primitives" = "xnone"; then LIBDRM_ATOMICS_NOT_FOUND_MSG($RADEON, radeon, Radeon, radeon) RADEON=no
+ LIBDRM_ATOMICS_NOT_FOUND_MSG($AMDGPU, amdgpu, AMD, amdgpu) + AMDGPU=no + LIBDRM_ATOMICS_NOT_FOUND_MSG($NOUVEAU, nouveau, NVIDIA, nouveau) NOUVEAU=no
@@ -257,6 +265,9 @@ else if test "x$RADEON" = xauto; then RADEON=yes fi + if test "x$AMDGPU" = xauto; then + AMDGPU=yes + fi if test "x$NOUVEAU" = xauto; then NOUVEAU=yes fi @@ -336,6 +347,11 @@ if test "x$RADEON" = xyes; then AC_DEFINE(HAVE_RADEON, 1, [Have radeon support]) fi
+AM_CONDITIONAL(HAVE_AMDGPU, [test "x$AMDGPU" = xyes]) +if test "x$AMDGPU" = xyes; then + AC_DEFINE(HAVE_AMDGPU, 1, [Have amdgpu support]) +fi + AM_CONDITIONAL(HAVE_TEGRA, [test "x$TEGRA" = xyes]) if test "x$TEGRA" = xyes; then AC_DEFINE(HAVE_TEGRA, 1, [Have Tegra support]) @@ -432,6 +448,8 @@ AC_CONFIG_FILES([ intel/libdrm_intel.pc radeon/Makefile radeon/libdrm_radeon.pc + amdgpu/Makefile + amdgpu/libdrm_amdgpu.pc nouveau/Makefile nouveau/libdrm_nouveau.pc omap/Makefile @@ -463,6 +481,7 @@ echo " libkms $LIBKMS" echo " Intel API $INTEL" echo " vmwgfx API $VMWGFX" echo " Radeon API $RADEON" +echo " AMDGPU API $AMDGPU" echo " Nouveau API $NOUVEAU" echo " OMAP API $OMAP" echo " EXYNOS API $EXYNOS" diff --git a/include/drm/amdgpu_drm.h b/include/drm/amdgpu_drm.h new file mode 100644 index 0000000..d248d77 --- /dev/null +++ b/include/drm/amdgpu_drm.h @@ -0,0 +1,600 @@ +/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: + * Kevin E. Martin martin@valinux.com + * Gareth Hughes gareth@valinux.com + * Keith Whitwell keith@tungstengraphics.com + */ + +#ifndef __AMDGPU_DRM_H__ +#define __AMDGPU_DRM_H__ + +#include <drm.h> + +#define DRM_AMDGPU_GEM_CREATE 0x00 +#define DRM_AMDGPU_GEM_MMAP 0x01 +#define DRM_AMDGPU_CTX 0x02 +#define DRM_AMDGPU_BO_LIST 0x03 +#define DRM_AMDGPU_CS 0x04 +#define DRM_AMDGPU_INFO 0x05 +#define DRM_AMDGPU_GEM_METADATA 0x06 +#define DRM_AMDGPU_GEM_WAIT_IDLE 0x07 +#define DRM_AMDGPU_GEM_VA 0x08 +#define DRM_AMDGPU_WAIT_CS 0x09 +#define DRM_AMDGPU_GEM_OP 0x10 +#define DRM_AMDGPU_GEM_USERPTR 0x11 + +#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) +#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) +#define DRM_IOCTL_AMDGPU_CTX DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx) +#define DRM_IOCTL_AMDGPU_BO_LIST DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list) +#define DRM_IOCTL_AMDGPU_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs) +#define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info) +#define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata) +#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle) +#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, union drm_amdgpu_gem_va) +#define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs) +#define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op) +#define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) + +#define AMDGPU_GEM_DOMAIN_CPU 0x1 +#define AMDGPU_GEM_DOMAIN_GTT 0x2 +#define AMDGPU_GEM_DOMAIN_VRAM 0x4 +#define AMDGPU_GEM_DOMAIN_GDS 0x8 +#define AMDGPU_GEM_DOMAIN_GWS 0x10 +#define AMDGPU_GEM_DOMAIN_OA 0x20 + +#define AMDGPU_GEM_DOMAIN_MASK 0x3F + +/* Flag that CPU access will be required for the case of VRAM domain */ +#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) +/* Flag that CPU access will not work, this VRAM domain is invisible */ +#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) +/* Flag that un-cached attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_UC (1 << 2) +/* Flag that USWC attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_WC (1 << 3) + +/* Flag mask for GTT domain_flags */ +#define AMDGPU_GEM_CREATE_CPU_GTT_MASK \ + (AMDGPU_GEM_CREATE_CPU_GTT_WC | \ + AMDGPU_GEM_CREATE_CPU_GTT_UC | \ + AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | \ + AMDGPU_GEM_CREATE_NO_CPU_ACCESS) + +struct drm_amdgpu_gem_create_in { + /** the requested memory size */ + uint64_t bo_size; + /** physical start_addr alignment in bytes for some HW requirements */ + uint64_t alignment; + /** the requested memory domains */ + uint64_t domains; + /** allocation flags */ + uint64_t domain_flags; +}; + +struct drm_amdgpu_gem_create_out { + /** returned GEM object handle */ + uint32_t handle; +}; + +union drm_amdgpu_gem_create { + struct drm_amdgpu_gem_create_in in; + struct drm_amdgpu_gem_create_out out; +}; + +/** Opcode to create new residency list. */ +#define AMDGPU_BO_LIST_OP_CREATE 0 +/** Opcode to destroy previously created residency list */ +#define AMDGPU_BO_LIST_OP_DESTROY 1 +/** Opcode to update resource information in the list */ +#define AMDGPU_BO_LIST_OP_UPDATE 2 + +struct drm_amdgpu_bo_list_in { + /** Type of operation */ + uint32_t operation; + /** Handle of list or 0 if we want to create one */ + uint32_t list_handle; + /** Number of BOs in list */ + uint32_t bo_number; + /** Size of each element describing BO */ + uint32_t bo_info_size; + /** Pointer to array describing BOs */ + uint64_t bo_info_ptr; +}; + +struct drm_amdgpu_bo_list_entry { + /** Handle of BO */ + uint32_t bo_handle; + /** New (if specified) BO priority to be used during migration */ + uint32_t bo_priority; +}; + +struct drm_amdgpu_bo_list_out { + /** Handle of resource list */ + uint32_t list_handle; +}; + +union drm_amdgpu_bo_list { + struct drm_amdgpu_bo_list_in in; + struct drm_amdgpu_bo_list_out out; +}; + +/* context related */ +#define AMDGPU_CTX_OP_ALLOC_CTX 1 +#define AMDGPU_CTX_OP_FREE_CTX 2 +#define AMDGPU_CTX_OP_QUERY_STATE 3 + +#define AMDGPU_CTX_OP_STATE_RUNNING 1 + +struct drm_amdgpu_ctx_in { + uint32_t op; + uint32_t flags; + uint32_t ctx_id; + uint32_t pad; +}; + +union drm_amdgpu_ctx_out { + struct { + uint32_t ctx_id; + } alloc; + + struct { + uint64_t flags; + uint64_t hangs; + } state; +}; + +union drm_amdgpu_ctx { + struct drm_amdgpu_ctx_in in; + union drm_amdgpu_ctx_out out; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define AMDGPU_GEM_USERPTR_READONLY (1 << 0) +#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1) +#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2) +#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_amdgpu_gem_userptr { + uint64_t addr; + uint64_t size; + uint32_t flags; + uint32_t handle; +}; + +#define AMDGPU_TILING_MACRO 0x1 +#define AMDGPU_TILING_MICRO 0x2 +#define AMDGPU_TILING_SWAP_16BIT 0x4 +#define AMDGPU_TILING_R600_NO_SCANOUT AMDGPU_TILING_SWAP_16BIT +#define AMDGPU_TILING_SWAP_32BIT 0x8 +/* this object requires a surface when mapped - i.e. front buffer */ +#define AMDGPU_TILING_SURFACE 0x10 +#define AMDGPU_TILING_MICRO_SQUARE 0x20 +#define AMDGPU_TILING_EG_BANKW_SHIFT 8 +#define AMDGPU_TILING_EG_BANKW_MASK 0xf +#define AMDGPU_TILING_EG_BANKH_SHIFT 12 +#define AMDGPU_TILING_EG_BANKH_MASK 0xf +#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 +#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf +#define AMDGPU_TILING_EG_TILE_SPLIT_SHIFT 24 +#define AMDGPU_TILING_EG_TILE_SPLIT_MASK 0xf +#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 +#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf + +#define AMDGPU_GEM_METADATA_OP_SET_METADATA 1 +#define AMDGPU_GEM_METADATA_OP_GET_METADATA 2 + +/** The same structure is shared for input/output */ +struct drm_amdgpu_gem_metadata { + uint32_t handle; /* GEM Object handle */ + uint32_t op; /** Do we want get or set metadata */ + struct { + uint64_t flags; + uint64_t tiling_info; /* family specific tiling info */ + uint32_t data_size_bytes; + uint32_t data[64]; + } data; +}; + +struct drm_amdgpu_gem_mmap_in { + uint32_t handle; /** the GEM object handle */ +}; + +struct drm_amdgpu_gem_mmap_out { + uint64_t addr_ptr; /** mmap offset from the vma offset manager */ +}; + +union drm_amdgpu_gem_mmap { + struct drm_amdgpu_gem_mmap_in in; + struct drm_amdgpu_gem_mmap_out out; +}; + +struct drm_amdgpu_gem_wait_idle_in { + uint32_t handle; /* GEM object handle */ + uint32_t flags; + uint64_t timeout; /* Timeout to wait. If 0 then returned immediately with the status */ +}; + +struct drm_amdgpu_gem_wait_idle_out { + uint32_t status; /* BO status: 0 - BO is idle, 1 - BO is busy */ + uint32_t domain; /* Returned current memory domain */ +}; + +union drm_amdgpu_gem_wait_idle { + struct drm_amdgpu_gem_wait_idle_in in; + struct drm_amdgpu_gem_wait_idle_out out; +}; + +struct drm_amdgpu_wait_cs_in { + uint64_t handle; + uint64_t timeout; + uint32_t ip_type; + uint32_t ip_instance; + uint32_t ring; +}; + +struct drm_amdgpu_wait_cs_out { + uint64_t status; +}; + +union drm_amdgpu_wait_cs { + struct drm_amdgpu_wait_cs_in in; + struct drm_amdgpu_wait_cs_out out; +}; + +/* Sets or returns a value associated with a buffer. */ +struct drm_amdgpu_gem_op { + uint32_t handle; /* buffer */ + uint32_t op; /* AMDGPU_GEM_OP_* */ + uint64_t value; /* input or return value */ +}; + +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_INITIAL_DOMAIN 1 + +#define AMDGPU_VA_OP_MAP 1 +#define AMDGPU_VA_OP_UNMAP 2 + +#define AMDGPU_VA_RESULT_OK 0 +#define AMDGPU_VA_RESULT_ERROR 1 +#define AMDGPU_VA_RESULT_VA_INVALID_ALIGNMENT 2 + +/* Mapping flags */ +/* readable mapping */ +#define AMDGPU_VM_PAGE_READABLE (1 << 1) +/* writable mapping */ +#define AMDGPU_VM_PAGE_WRITEABLE (1 << 2) +/* executable mapping, new for VI */ +#define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) + +struct drm_amdgpu_gem_va_in { + /* GEM object handle */ + uint32_t handle; + uint32_t pad; + /* map or unmap*/ + uint32_t operation; + /* specify mapping flags */ + uint32_t flags; + /* va address to assign . Must be correctly aligned.*/ + uint64_t va_address; + /* Specify offset inside of BO to assign. Must be correctly aligned.*/ + uint64_t offset_in_bo; + /* Specify mapping size. If 0 and offset is 0 then map the whole BO.*/ + /* Must be correctly aligned. */ + uint64_t map_size; +}; + +struct drm_amdgpu_gem_va_out { + uint32_t result; +}; + +union drm_amdgpu_gem_va { + struct drm_amdgpu_gem_va_in in; + struct drm_amdgpu_gem_va_out out; +}; + +#define AMDGPU_HW_IP_GFX 0 +#define AMDGPU_HW_IP_COMPUTE 1 +#define AMDGPU_HW_IP_DMA 2 +#define AMDGPU_HW_IP_UVD 3 +#define AMDGPU_HW_IP_VCE 4 +#define AMDGPU_HW_IP_NUM 5 + +#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1 + +#define AMDGPU_CHUNK_ID_IB 0x01 +#define AMDGPU_CHUNK_ID_FENCE 0x02 +struct drm_amdgpu_cs_chunk { + uint32_t chunk_id; + uint32_t length_dw; + uint64_t chunk_data; +}; + +struct drm_amdgpu_cs_in { + /** Rendering context id */ + uint32_t ctx_id; + /** Handle of resource list associated with CS */ + uint32_t bo_list_handle; + uint32_t num_chunks; + uint32_t pad; + /* this points to uint64_t * which point to cs chunks */ + uint64_t chunks; +}; + +struct drm_amdgpu_cs_out { + uint64_t handle; +}; + +union drm_amdgpu_cs { + struct drm_amdgpu_cs_in in; + struct drm_amdgpu_cs_out out; +}; + +/* Specify flags to be used for IB */ + +/* This IB should be submitted to CE */ +#define AMDGPU_IB_FLAG_CE (1<<0) + +/* GDS is used by this IB */ +#define AMDGPU_IB_FLAG_GDS (1<<1) + +struct drm_amdgpu_cs_chunk_ib { + /** + * Handle of GEM object to be used as IB or 0 if it is already in + * residency list. + */ + uint32_t handle; + uint32_t flags; /* IB Flags */ + uint64_t va_start; /* Virtual address to begin IB execution */ + uint32_t ib_bytes; /* Size of submission */ + uint32_t ip_type; /* HW IP to submit to */ + uint32_t ip_instance; /* HW IP index of the same type to submit to */ + uint32_t ring; /* Ring index to submit to */ +}; + +struct drm_amdgpu_cs_chunk_fence { + uint32_t handle; + uint32_t offset; +}; + +struct drm_amdgpu_cs_chunk_data { + union { + struct drm_amdgpu_cs_chunk_ib ib_data; + struct drm_amdgpu_cs_chunk_fence fence_data; + }; +}; + +/** + * Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU + * + */ +#define AMDGPU_IDS_FLAGS_FUSION 0x1 + +/* indicate if acceleration can be working */ +#define AMDGPU_INFO_ACCEL_WORKING 0x00 +/* get the crtc_id from the mode object id? */ +#define AMDGPU_INFO_CRTC_FROM_ID 0x01 +/* query hw IP info */ +#define AMDGPU_INFO_HW_IP_INFO 0x02 +/* query hw IP instance count for the specified type */ +#define AMDGPU_INFO_HW_IP_COUNT 0x03 +/* timestamp for GL_ARB_timer_query */ +#define AMDGPU_INFO_TIMESTAMP 0x05 +/* Query the firmware version */ +#define AMDGPU_INFO_FW_VERSION 0x0e + /* Subquery id: Query VCE firmware version */ + #define AMDGPU_INFO_FW_VCE 0x1 + /* Subquery id: Query UVD firmware version */ + #define AMDGPU_INFO_FW_UVD 0x2 + /* Subquery id: Query GMC firmware version */ + #define AMDGPU_INFO_FW_GMC 0x03 + /* Subquery id: Query GFX ME firmware version */ + #define AMDGPU_INFO_FW_GFX_ME 0x04 + /* Subquery id: Query GFX PFP firmware version */ + #define AMDGPU_INFO_FW_GFX_PFP 0x05 + /* Subquery id: Query GFX CE firmware version */ + #define AMDGPU_INFO_FW_GFX_CE 0x06 + /* Subquery id: Query GFX RLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC 0x07 + /* Subquery id: Query GFX MEC firmware version */ + #define AMDGPU_INFO_FW_GFX_MEC 0x08 + /* Subquery id: Query SMC firmware version */ + #define AMDGPU_INFO_FW_SMC 0x0a + /* Subquery id: Query SDMA firmware version */ + #define AMDGPU_INFO_FW_SDMA 0x0b +/* number of bytes moved for TTM migration */ +#define AMDGPU_INFO_NUM_BYTES_MOVED 0x0f +/* the used VRAM size */ +#define AMDGPU_INFO_VRAM_USAGE 0x10 +/* the used GTT size */ +#define AMDGPU_INFO_GTT_USAGE 0x11 +/* Information about GDS, etc. resource configuration */ +#define AMDGPU_INFO_GDS_CONFIG 0x13 +/* Query information about VRAM and GTT domains */ +#define AMDGPU_INFO_VRAM_GTT 0x14 +/* Query information about register in MMR address space*/ +#define AMDGPU_INFO_READ_MMR_REG 0x15 +/* Query information about device: rev id, family, etc. */ +#define AMDGPU_INFO_DEV_INFO 0x16 +/* visible vram usage */ +#define AMDGPU_INFO_VIS_VRAM_USAGE 0x17 + +#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0 +#define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff +#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT 8 +#define AMDGPU_INFO_MMR_SH_INDEX_MASK 0xff + +/* Input structure for the INFO ioctl */ +struct drm_amdgpu_info { + /* Where the return value will be stored */ + uint64_t return_pointer; + /* The size of the return value. Just like "size" in "snprintf", + * it limits how many bytes the kernel can write. */ + uint32_t return_size; + /* The query request id. */ + uint32_t query; + + union { + struct { + uint32_t id; + } mode_crtc; + + struct { + /** AMDGPU_HW_IP_* */ + uint32_t type; + /** + * Index of the IP if there are more IPs of the same type. + * Ignored by AMDGPU_INFO_HW_IP_COUNT. + */ + uint32_t ip_instance; + } query_hw_ip; + + struct { + uint32_t dword_offset; + uint32_t count; /* number of registers to read */ + uint32_t instance; + uint32_t flags; + } read_mmr_reg; + + struct { + /** AMDGPU_INFO_FW_* */ + uint32_t fw_type; + /** Index of the IP if there are more IPs of the same type. */ + uint32_t ip_instance; + /** + * Index of the engine. Whether this is used depends + * on the firmware type. (e.g. MEC, SDMA) + */ + uint32_t index; + } query_fw; + }; +}; + +struct drm_amdgpu_info_gds { + /** GDS GFX partition size */ + uint32_t gds_gfx_partition_size; + /** GDS compute partition size */ + uint32_t compute_partition_size; + /** total GDS memory size */ + uint32_t gds_total_size; + /** GWS size per GFX partition */ + uint32_t gws_per_gfx_partition; + /** GSW size per compute partition */ + uint32_t gws_per_compute_partition; + /** OA size per GFX partition */ + uint32_t oa_per_gfx_partition; + /** OA size per compute partition */ + uint32_t oa_per_compute_partition; +}; + +struct drm_amdgpu_info_vram_gtt { + uint64_t vram_size; + uint64_t vram_cpu_accessible_size; + uint64_t gtt_size; +}; + +struct drm_amdgpu_info_firmware { + uint32_t ver; + uint32_t feature; +}; + +struct drm_amdgpu_info_device { + /** PCI Device ID */ + uint32_t device_id; + /** Internal chip revision: A0, A1, etc.) */ + uint32_t chip_rev; + uint32_t external_rev; + /** Revision id in PCI Config space */ + uint32_t pci_rev; + uint32_t family; + uint32_t num_shader_engines; + uint32_t num_shader_arrays_per_engine; + uint32_t gpu_counter_freq; /* in KHz */ + uint64_t max_engine_clock; /* in KHz */ + /* cu information */ + uint32_t cu_active_number; + uint32_t cu_ao_mask; + uint32_t cu_bitmap[4][4]; + /** Render backend pipe mask. One render backend is CB+DB. */ + uint32_t enabled_rb_pipes_mask; + uint32_t num_rb_pipes; + uint32_t num_hw_gfx_contexts; + uint32_t _pad; + uint64_t ids_flags; + /** Starting virtual address for UMDs. */ + uint64_t virtual_address_offset; + /** Required alignment of virtual addresses. */ + uint32_t virtual_address_alignment; + /** Page table entry - fragment size */ + uint32_t pte_fragment_size; + uint32_t gart_page_size; +}; + +struct drm_amdgpu_info_hw_ip { + /** Version of h/w IP */ + uint32_t hw_ip_version_major; + uint32_t hw_ip_version_minor; + /** Capabilities */ + uint64_t capabilities_flags; + /** Bitmask of available rings. Bit 0 means ring 0, etc. */ + uint32_t available_rings; +}; + +/* Those correspond to the tile index to use, this is to explicitly state + * the API that is implicitly defined by the tile mode array. + */ +#define SI_TILE_MODE_COLOR_LINEAR_ALIGNED 8 +#define SI_TILE_MODE_COLOR_1D 13 +#define SI_TILE_MODE_COLOR_1D_SCANOUT 9 +#define SI_TILE_MODE_COLOR_2D_8BPP 14 +#define SI_TILE_MODE_COLOR_2D_16BPP 15 +#define SI_TILE_MODE_COLOR_2D_32BPP 16 +#define SI_TILE_MODE_COLOR_2D_64BPP 17 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP 11 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP 12 +#define SI_TILE_MODE_DEPTH_STENCIL_1D 4 +#define SI_TILE_MODE_DEPTH_STENCIL_2D 0 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_2AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_4AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA 2 + +#define CIK_TILE_MODE_DEPTH_STENCIL_1D 5 + +/* + * Supported GPU families + */ +#define AMDGPU_FAMILY_UNKNOWN 0 +#define AMDGPU_FAMILY_CI 120 /* Bonaire, Hawaii */ +#define AMDGPU_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */ +#define AMDGPU_FAMILY_VI 130 /* Iceland, Tonga */ +#define AMDGPU_FAMILY_CZ 135 /* Carrizo */ + +#endif
This adds some basic unit tests for the new amdgpu driver.
v2: use common util_math.h
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- configure.ac | 23 ++ tests/Makefile.am | 6 + tests/amdgpu/Makefile.am | 24 ++ tests/amdgpu/amdgpu_test.c | 241 +++++++++++++ tests/amdgpu/amdgpu_test.h | 119 +++++++ tests/amdgpu/basic_tests.c | 676 ++++++++++++++++++++++++++++++++++++ tests/amdgpu/bo_tests.c | 151 ++++++++ tests/amdgpu/cs_tests.c | 319 +++++++++++++++++ tests/amdgpu/uvd_messages.h | 813 ++++++++++++++++++++++++++++++++++++++++++++ tests/kmstest/main.c | 1 + 10 files changed, 2373 insertions(+) create mode 100644 tests/amdgpu/Makefile.am create mode 100644 tests/amdgpu/amdgpu_test.c create mode 100644 tests/amdgpu/amdgpu_test.h create mode 100644 tests/amdgpu/basic_tests.c create mode 100644 tests/amdgpu/bo_tests.c create mode 100644 tests/amdgpu/cs_tests.c create mode 100644 tests/amdgpu/uvd_messages.h
diff --git a/configure.ac b/configure.ac index a8997c6..8bd12ea 100644 --- a/configure.ac +++ b/configure.ac @@ -350,6 +350,28 @@ fi AM_CONDITIONAL(HAVE_AMDGPU, [test "x$AMDGPU" = xyes]) if test "x$AMDGPU" = xyes; then AC_DEFINE(HAVE_AMDGPU, 1, [Have amdgpu support]) + + # Detect cunit library + PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no]) + + # If pkg-config does not find cunit, check it using AC_CHECK_LIB. We + # do this because Debian (Ubuntu) lacks pkg-config file for cunit. + if test "x${have_cunit}" = "xno"; then + AC_CHECK_LIB([cunit], [CU_initialize_registry], [have_cunit=yes], [have_cunit=no]) + if test "x${have_cunit}" = "xyes"; then + CUNIT_LIBS="-lcunit" + CUNIT_CFLAGS="" + AC_SUBST([CUNIT_LIBS]) + AC_SUBST([CUNIT_CFLAGS]) + fi + fi + + AM_CONDITIONAL(HAVE_CUNIT, [test "x$have_cunit" != "xno"]) + AC_DEFINE(HAVE_CUNIT, [test "x$have_cunit" != "xno"], [Enable CUNIT Have amdgpu support]) + + if test "x$have_cunit" = "xno"; then + AC_MSG_WARN([Could not find cunit library. Disabling amdgpu tests]) + fi fi
AM_CONDITIONAL(HAVE_TEGRA, [test "x$TEGRA" = xyes]) @@ -466,6 +488,7 @@ AC_CONFIG_FILES([ tests/kmstest/Makefile tests/proptest/Makefile tests/radeon/Makefile + tests/amdgpu/Makefile tests/vbltest/Makefile tests/exynos/Makefile tests/tegra/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index 069285f..a980b3d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,6 +8,12 @@ if HAVE_RADEON SUBDIRS += radeon endif
+if HAVE_AMDGPU +if HAVE_CUNIT +SUBDIRS += amdgpu +endif +endif + if HAVE_EXYNOS SUBDIRS += exynos endif diff --git a/tests/amdgpu/Makefile.am b/tests/amdgpu/Makefile.am new file mode 100644 index 0000000..ba7339d --- /dev/null +++ b/tests/amdgpu/Makefile.am @@ -0,0 +1,24 @@ +AM_CFLAGS = \ + -I $(top_srcdir)/include/drm \ + -I $(top_srcdir)/amdgpu \ + -I $(top_srcdir) + +LDADD = $(top_builddir)/libdrm.la \ + $(top_builddir)/amdgpu/libdrm_amdgpu.la + +if HAVE_INSTALL_TESTS +bin_PROGRAMS = \ + amdgpu_test +else +noinst_PROGRAMS = \ + amdgpu_test +endif + +amdgpu_test_CPPFLAGS = $(CUNIT_CFLAGS) +amdgpu_test_LDFLAGS = $(CUNIT_LIBS) + +amdgpu_test_SOURCES = \ + amdgpu_test.c \ + basic_tests.c \ + bo_tests.c \ + cs_tests.c diff --git a/tests/amdgpu/amdgpu_test.c b/tests/amdgpu/amdgpu_test.c new file mode 100644 index 0000000..fc14b70 --- /dev/null +++ b/tests/amdgpu/amdgpu_test.c @@ -0,0 +1,241 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <stdarg.h> +#include <stdint.h> + +#include "drm.h" +#include "xf86drmMode.h" +#include "xf86drm.h" + +#include "CUnit/Basic.h" + +#include "amdgpu_test.h" + +/** + * Open handles for amdgpu devices + * + */ +int drm_amdgpu[MAX_CARDS_SUPPORTED]; + +/** The table of all known test suites to run */ +CU_SuiteInfo suites[] = { + { "Basic Tests", suite_basic_tests_init, + suite_basic_tests_clean, basic_tests }, + { "BO Tests", suite_bo_tests_init, + suite_bo_tests_clean, bo_tests }, + { "CS Tests", suite_cs_tests_init, + suite_cs_tests_clean, cs_tests }, + CU_SUITE_INFO_NULL, +}; + + +/** Display information about all suites and their tests */ +static void display_test_suites(void) +{ + int iSuite; + int iTest; + + printf("Suites\n"); + + for (iSuite = 0; suites[iSuite].pName != NULL; iSuite++) { + printf("Suite id = %d: Name '%s'\n", + iSuite + 1, suites[iSuite].pName); + + for (iTest = 0; suites[iSuite].pTests[iTest].pName != NULL; + iTest++) { + printf(" Test id %d: Name: '%s'\n", iTest + 1, + suites[iSuite].pTests[iTest].pName); + } + } +} + + +/** Help string for command line parameters */ +static const char usage[] = "Usage: %s [-hl] [<-s <suite id>> [-t <test id>]]\n" + "where:\n" + " l - Display all suites and their tests\n" + " h - Display this help\n"; +/** Specified options strings for getopt */ +static const char options[] = "hls:t:"; + +/* The main() function for setting up and running the tests. + * Returns a CUE_SUCCESS on successful running, another + * CUnit error code on failure. + */ +int main(int argc, char **argv) +{ + int c; /* Character received from getopt */ + int i = 0; + int suite_id = -1; /* By default run everything */ + int test_id = -1; /* By default run all tests in the suite */ + CU_pSuite pSuite = NULL; + CU_pTest pTest = NULL; + + int aval = drmAvailable(); + + if (aval == 0) { + fprintf(stderr, "DRM driver is not available\n"); + exit(EXIT_FAILURE); + } + + + for (i = 0; i < MAX_CARDS_SUPPORTED; i++) + drm_amdgpu[i] = 0; + + + /* Parse command line string */ + opterr = 0; /* Do not print error messages from getopt */ + while ((c = getopt(argc, argv, options)) != -1) { + switch (c) { + case 'l': + display_test_suites(); + exit(EXIT_SUCCESS); + case 's': + suite_id = atoi(optarg); + break; + case 't': + test_id = atoi(optarg); + break; + case '?': + case 'h': + fprintf(stderr, usage, argv[0]); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, usage, argv[0]); + exit(EXIT_FAILURE); + } + } + + /* Try to open all possible radeon connections + * Right now: Open only the 0. + */ + printf("Try to open the card 0..\n"); + drm_amdgpu[0] = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); + + if (drm_amdgpu[0] == 1) { + perror("Cannot open /dev/dri/card0\n"); + exit(EXIT_FAILURE); + } + + /** Display version of DRM driver */ + drmVersionPtr retval; + drm_version_t *version = drmMalloc(sizeof(*version)); + + version->name_len = 0; + version->name = NULL; + version->date_len = 0; + version->date = NULL; + version->desc_len = 0; + version->desc = NULL; + + if (drmIoctl(drm_amdgpu[0], DRM_IOCTL_VERSION, version)) { + perror("Could not get DRM driver version\n"); + drmFree(version); + exit(EXIT_FAILURE); + } + + if (version->name_len) + version->name = drmMalloc(version->name_len + 1); + if (version->date_len) + version->date = drmMalloc(version->date_len + 1); + if (version->desc_len) + version->desc = drmMalloc(version->desc_len + 1); + + if (drmIoctl(drm_amdgpu[0], DRM_IOCTL_VERSION, version)) { + perror("Could not get information about DRM driver"); + drmFree(version); + exit(EXIT_FAILURE); + } + + /* The results might not be null-terminated strings. Add zero */ + if (version->name_len) + version->name[version->name_len] = '\0'; + if (version->date_len) + version->date[version->date_len] = '\0'; + if (version->desc_len) + version->desc[version->desc_len] = '\0'; + + printf("DRM Driver: Name: [%s] : Date [%s] : Description [%s]\n", + version->name, version->date, version->desc); + + drmFree(version); + + /* Initialize test suites to run */ + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* Register suites. */ + if (CU_register_suites(suites) != CUE_SUCCESS) { + fprintf(stderr, "suite registration failed - %s\n", + CU_get_error_msg()); + exit(EXIT_FAILURE); + } + + /* Run tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + + if (suite_id != -1) { /* If user specify particular suite? */ + pSuite = CU_get_suite_by_index((unsigned int) suite_id, + CU_get_registry()); + + if (pSuite) { + if (test_id != -1) { /* If user specify test id */ + pTest = CU_get_test_by_index( + (unsigned int) test_id, + pSuite); + if (pTest) + CU_basic_run_test(pSuite, pTest); + else { + fprintf(stderr, "Invalid test id: %d\n", + test_id); + exit(EXIT_FAILURE); + } + } else + CU_basic_run_suite(pSuite); + } else { + fprintf(stderr, "Invalid suite id : %d\n", + suite_id); + exit(EXIT_FAILURE); + } + } else + CU_basic_run_tests(); + + CU_cleanup_registry(); + return CU_get_error(); +} diff --git a/tests/amdgpu/amdgpu_test.h b/tests/amdgpu/amdgpu_test.h new file mode 100644 index 0000000..508e561 --- /dev/null +++ b/tests/amdgpu/amdgpu_test.h @@ -0,0 +1,119 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _amdgpu_test_h_ +#define _amdgpu_test_h_ + +#include "amdgpu.h" + +/** + * Define max. number of card in system which we are able to handle + */ +#define MAX_CARDS_SUPPORTED 4 + +/* Forward reference for array to keep "drm" handles */ +extern int drm_amdgpu[MAX_CARDS_SUPPORTED]; + +/************************* Basic test suite ********************************/ + +/* + * Define basic test suite to serve as the starting point for future testing +*/ + +/** + * Initialize basic test suite + */ +int suite_basic_tests_init(); + +/** + * Deinitialize basic test suite + */ +int suite_basic_tests_clean(); + +/** + * Tests in basic test suite + */ +extern CU_TestInfo basic_tests[]; + +/** + * Initialize bo test suite + */ +int suite_bo_tests_init(); + +/** + * Deinitialize bo test suite + */ +int suite_bo_tests_clean(); + +/** + * Tests in bo test suite + */ +extern CU_TestInfo bo_tests[]; + +/** + * Initialize cs test suite + */ +int suite_cs_tests_init(); + +/** + * Deinitialize cs test suite + */ +int suite_cs_tests_clean(); + +/** + * Tests in cs test suite + */ +extern CU_TestInfo cs_tests[]; + +/** + * Helper functions + */ +static inline amdgpu_bo_handle gpu_mem_alloc( + amdgpu_device_handle device_handle, + uint64_t size, + uint64_t alignment, + uint32_t type, + uint64_t flags, + uint64_t *vmc_addr) +{ + struct amdgpu_bo_alloc_request req = {0}; + struct amdgpu_bo_alloc_result res = {0}; + int r; + + CU_ASSERT_NOT_EQUAL(vmc_addr, NULL); + + req.alloc_size = size; + req.phys_alignment = alignment; + req.preferred_heap = type; + req.flags = flags; + + r = amdgpu_bo_alloc(device_handle, &req, &res); + CU_ASSERT_EQUAL(r, 0); + + CU_ASSERT_NOT_EQUAL(res.virtual_mc_base_address, 0); + CU_ASSERT_NOT_EQUAL(res.buf_handle, NULL); + *vmc_addr = res.virtual_mc_base_address; + return res.buf_handle; +} + +#endif /* #ifdef _amdgpu_test_h_ */ diff --git a/tests/amdgpu/basic_tests.c b/tests/amdgpu/basic_tests.c new file mode 100644 index 0000000..c53f6a0 --- /dev/null +++ b/tests/amdgpu/basic_tests.c @@ -0,0 +1,676 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "CUnit/Basic.h" + +#include "amdgpu_test.h" +#include "amdgpu_drm.h" + +static amdgpu_device_handle device_handle; +static uint32_t major_version; +static uint32_t minor_version; + +static void amdgpu_query_info_test(void); +static void amdgpu_memory_alloc(void); +static void amdgpu_command_submission_gfx(void); +static void amdgpu_command_submission_compute(void); +static void amdgpu_command_submission_sdma(void); +static void amdgpu_userptr_test(void); + +CU_TestInfo basic_tests[] = { + { "Query Info Test", amdgpu_query_info_test }, + { "Memory alloc Test", amdgpu_memory_alloc }, + { "Userptr Test", amdgpu_userptr_test }, + { "Command submission Test (GFX)", amdgpu_command_submission_gfx }, + { "Command submission Test (Compute)", amdgpu_command_submission_compute }, + { "Command submission Test (SDMA)", amdgpu_command_submission_sdma }, + CU_TEST_INFO_NULL, +}; +#define BUFFER_SIZE (8 * 1024) +#define SDMA_PKT_HEADER_op_offset 0 +#define SDMA_PKT_HEADER_op_mask 0x000000FF +#define SDMA_PKT_HEADER_op_shift 0 +#define SDMA_PKT_HEADER_OP(x) (((x) & SDMA_PKT_HEADER_op_mask) << SDMA_PKT_HEADER_op_shift) +#define SDMA_OPCODE_CONSTANT_FILL 11 +# define SDMA_CONSTANT_FILL_EXTRA_SIZE(x) ((x) << 14) + /* 0 = byte fill + * 2 = DW fill + */ +#define SDMA_PACKET(op, sub_op, e) ((((e) & 0xFFFF) << 16) | \ + (((sub_op) & 0xFF) << 8) | \ + (((op) & 0xFF) << 0)) +#define SDMA_OPCODE_WRITE 2 +# define SDMA_WRITE_SUB_OPCODE_LINEAR 0 +# define SDMA_WRTIE_SUB_OPCODE_TILED 1 + +#define SDMA_OPCODE_COPY 1 +# define SDMA_COPY_SUB_OPCODE_LINEAR 0 + +int suite_basic_tests_init(void) +{ + int r; + + r = amdgpu_device_initialize(drm_amdgpu[0], &major_version, + &minor_version, &device_handle); + + if (r == 0) + return CUE_SUCCESS; + else + return CUE_SINIT_FAILED; +} + +int suite_basic_tests_clean(void) +{ + int r = amdgpu_device_deinitialize(device_handle); + + if (r == 0) + return CUE_SUCCESS; + else + return CUE_SCLEAN_FAILED; +} + +static void amdgpu_query_info_test(void) +{ + struct amdgpu_gpu_info gpu_info = {0}; + uint32_t version, feature; + int r; + + r = amdgpu_query_gpu_info(device_handle, &gpu_info); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_query_firmware_version(device_handle, AMDGPU_INFO_FW_VCE, 0, + 0, &version, &feature); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_memory_alloc(void) +{ + amdgpu_bo_handle bo; + uint64_t bo_mc; + int r; + + /* Test visible VRAM */ + bo = gpu_mem_alloc(device_handle, + 4096, 4096, + AMDGPU_GEM_DOMAIN_VRAM, + AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, + &bo_mc); + + r = amdgpu_bo_free(bo); + CU_ASSERT_EQUAL(r, 0); + + /* Test invisible VRAM */ + bo = gpu_mem_alloc(device_handle, + 4096, 4096, + AMDGPU_GEM_DOMAIN_VRAM, + AMDGPU_GEM_CREATE_NO_CPU_ACCESS, + &bo_mc); + + r = amdgpu_bo_free(bo); + CU_ASSERT_EQUAL(r, 0); + + /* Test GART Cacheable */ + bo = gpu_mem_alloc(device_handle, + 4096, 4096, + AMDGPU_GEM_DOMAIN_GTT, + 0, + &bo_mc); + + r = amdgpu_bo_free(bo); + CU_ASSERT_EQUAL(r, 0); + + /* Test GART USWC */ + bo = gpu_mem_alloc(device_handle, + 4096, 4096, + AMDGPU_GEM_DOMAIN_GTT, + AMDGPU_GEM_CREATE_CPU_GTT_WC, + &bo_mc); + + r = amdgpu_bo_free(bo); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_command_submission_gfx(void) +{ + amdgpu_context_handle context_handle; + struct amdgpu_cs_ib_alloc_result ib_result = {0}; + struct amdgpu_cs_ib_alloc_result ib_result_ce = {0}; + struct amdgpu_cs_request ibs_request = {0}; + struct amdgpu_cs_ib_info ib_info[2]; + struct amdgpu_cs_query_fence fence_status = {0}; + uint32_t *ptr; + uint32_t expired; + int r; + + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_cs_alloc_ib(device_handle, context_handle, + amdgpu_cs_ib_size_4K, &ib_result); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_cs_alloc_ib(device_handle, context_handle, + amdgpu_cs_ib_size_4K, &ib_result_ce); + CU_ASSERT_EQUAL(r, 0); + + memset(ib_info, 0, 2 * sizeof(struct amdgpu_cs_ib_info)); + + /* IT_SET_CE_DE_COUNTERS */ + ptr = ib_result_ce.cpu; + ptr[0] = 0xc0008900; + ptr[1] = 0; + ptr[2] = 0xc0008400; + ptr[3] = 1; + ib_info[0].ib_handle = ib_result_ce.handle; + ib_info[0].size = 4; + ib_info[0].flags = AMDGPU_CS_GFX_IB_CE; + + /* IT_WAIT_ON_CE_COUNTER */ + ptr = ib_result.cpu; + ptr[0] = 0xc0008600; + ptr[1] = 0x00000001; + ib_info[1].ib_handle = ib_result.handle; + ib_info[1].size = 2; + + ibs_request.ip_type = AMDGPU_HW_IP_GFX; + ibs_request.number_of_ibs = 2; + ibs_request.ibs = ib_info; + + r = amdgpu_cs_submit(device_handle, context_handle, 0, + &ibs_request, 1, &fence_status.fence); + CU_ASSERT_EQUAL(r, 0); + + fence_status.context = context_handle; + fence_status.timeout_ns = AMDGPU_TIMEOUT_INFINITE; + fence_status.ip_type = AMDGPU_HW_IP_GFX; + + r = amdgpu_cs_query_fence_status(device_handle, &fence_status, &expired); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_cs_ctx_free(device_handle, context_handle); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_command_submission_compute(void) +{ + amdgpu_context_handle context_handle; + struct amdgpu_cs_ib_alloc_result ib_result; + struct amdgpu_cs_request ibs_request; + struct amdgpu_cs_ib_info ib_info; + struct amdgpu_cs_query_fence fence_status; + uint32_t *ptr; + uint32_t expired; + int i, r, instance; + + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + CU_ASSERT_EQUAL(r, 0); + + for (instance = 0; instance < 8; instance++) { + memset(&ib_result, 0, sizeof(struct amdgpu_cs_ib_alloc_result)); + r = amdgpu_cs_alloc_ib(device_handle, context_handle, + amdgpu_cs_ib_size_4K, &ib_result); + CU_ASSERT_EQUAL(r, 0); + + ptr = ib_result.cpu; + for (i = 0; i < 16; ++i) + ptr[i] = 0xffff1000; + + memset(&ib_info, 0, sizeof(struct amdgpu_cs_ib_info)); + ib_info.ib_handle = ib_result.handle; + ib_info.size = 16; + + memset(&ibs_request, 0, sizeof(struct amdgpu_cs_request)); + ibs_request.ip_type = AMDGPU_HW_IP_COMPUTE; + ibs_request.ring = instance; + ibs_request.number_of_ibs = 1; + ibs_request.ibs = &ib_info; + + memset(&fence_status, 0, sizeof(struct amdgpu_cs_query_fence)); + r = amdgpu_cs_submit(device_handle, context_handle, 0, + &ibs_request, 1, &fence_status.fence); + CU_ASSERT_EQUAL(r, 0); + + fence_status.context = context_handle; + fence_status.timeout_ns = AMDGPU_TIMEOUT_INFINITE; + fence_status.ip_type = AMDGPU_HW_IP_COMPUTE; + fence_status.ring = instance; + + r = amdgpu_cs_query_fence_status(device_handle, &fence_status, &expired); + CU_ASSERT_EQUAL(r, 0); + } + + r = amdgpu_cs_ctx_free(device_handle, context_handle); + CU_ASSERT_EQUAL(r, 0); +} + +/* + * caller need create/release: + * pm4_src, resources, ib_info, and ibs_request + * submit command stream described in ibs_request and wait for this IB accomplished + */ +static void amdgpu_sdma_test_exec_cs(amdgpu_context_handle context_handle, + int instance, int pm4_dw, uint32_t *pm4_src, + int res_cnt, amdgpu_bo_handle *resources, + struct amdgpu_cs_ib_info *ib_info, + struct amdgpu_cs_request *ibs_request) +{ + int r, i, j; + uint32_t expired; + uint32_t *ring_ptr; + struct amdgpu_cs_ib_alloc_result ib_result = {0}; + struct amdgpu_cs_query_fence fence_status = {0}; + + /* prepare CS */ + CU_ASSERT_NOT_EQUAL(pm4_src, NULL); + CU_ASSERT_NOT_EQUAL(resources, NULL); + CU_ASSERT_NOT_EQUAL(ib_info, NULL); + CU_ASSERT_NOT_EQUAL(ibs_request, NULL); + CU_ASSERT_TRUE(pm4_dw <= 1024); + + /* allocate IB */ + r = amdgpu_cs_alloc_ib(device_handle, context_handle, + amdgpu_cs_ib_size_4K, &ib_result); + CU_ASSERT_EQUAL(r, 0); + + /* copy PM4 packet to ring from caller */ + ring_ptr = ib_result.cpu; + memcpy(ring_ptr, pm4_src, pm4_dw * sizeof(*pm4_src)); + + ib_info->ib_handle = ib_result.handle; + ib_info->size = pm4_dw; + + ibs_request->ip_type = AMDGPU_HW_IP_DMA; + ibs_request->ring = instance; + ibs_request->number_of_ibs = 1; + ibs_request->ibs = ib_info; + ibs_request->number_of_resources = res_cnt; + ibs_request->resources = resources; + + + CU_ASSERT_NOT_EQUAL(ibs_request, NULL); + + /* submit CS */ + r = amdgpu_cs_submit(device_handle, context_handle, 0, + ibs_request, 1, &fence_status.fence); + CU_ASSERT_EQUAL(r, 0); + + fence_status.ip_type = AMDGPU_HW_IP_DMA; + fence_status.ring = ibs_request->ring; + fence_status.context = context_handle; + fence_status.timeout_ns = AMDGPU_TIMEOUT_INFINITE; + + /* wait for IB accomplished */ + r = amdgpu_cs_query_fence_status(device_handle, &fence_status, + &expired); + CU_ASSERT_EQUAL(r, 0); + CU_ASSERT_EQUAL(expired, true); +} + +static void amdgpu_command_submission_sdma_write_linear(void) +{ + const int sdma_write_length = 128; + const int pm4_dw = 256; + amdgpu_context_handle context_handle; + amdgpu_bo_handle bo; + amdgpu_bo_handle *resources; + uint32_t *pm4; + struct amdgpu_cs_ib_info *ib_info; + struct amdgpu_cs_request *ibs_request; + uint64_t bo_mc; + volatile uint32_t *bo_cpu; + int i, j, r, loop; + uint64_t gtt_flags[3] = {0, AMDGPU_GEM_CREATE_CPU_GTT_UC, + AMDGPU_GEM_CREATE_CPU_GTT_WC}; + + pm4 = malloc(pm4_dw * 4); + CU_ASSERT_NOT_EQUAL(pm4, NULL); + + ib_info = malloc(sizeof(*ib_info)); + CU_ASSERT_NOT_EQUAL(ib_info, NULL); + memset(ib_info, 0, sizeof(*ib_info)); + + ibs_request = malloc(sizeof(*ibs_request)); + CU_ASSERT_NOT_EQUAL(ibs_request, NULL); + memset(ibs_request, 0, sizeof(*ibs_request)); + + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + CU_ASSERT_EQUAL(r, 0); + + /* prepare resource */ + resources = malloc(sizeof(amdgpu_bo_handle)); + CU_ASSERT_NOT_EQUAL(resources, NULL); + + loop = 0; + while(loop < 3) { + /* allocate UC bo for sDMA use */ + bo = gpu_mem_alloc(device_handle, + sdma_write_length * sizeof(uint32_t), + 4096, AMDGPU_GEM_DOMAIN_GTT, + gtt_flags[loop], &bo_mc); + + CU_ASSERT_EQUAL(amdgpu_bo_cpu_map(bo, (void **)&bo_cpu), 0); + CU_ASSERT_NOT_EQUAL(bo_cpu, NULL); + + /* clear bo */ + memset((void*)bo_cpu, 0, sdma_write_length * sizeof(uint32_t)); + + + resources[0] = bo; + + /* fullfill PM4: test DMA write-linear */ + i = j = 0; + pm4[i++] = SDMA_PACKET(SDMA_OPCODE_WRITE, + SDMA_WRITE_SUB_OPCODE_LINEAR, 0); + pm4[i++] = 0xffffffff & bo_mc; + pm4[i++] = (0xffffffff00000000 & bo_mc) >> 32; + pm4[i++] = sdma_write_length; + while(j++ < sdma_write_length) + pm4[i++] = 0xdeadbeaf; + + amdgpu_sdma_test_exec_cs(context_handle, 0, + i, pm4, + 1, resources, + ib_info, ibs_request); + + /* verify if SDMA test result meets with expected */ + i = 0; + while(i < sdma_write_length) { + CU_ASSERT_EQUAL(bo_cpu[i++], 0xdeadbeaf); + } + amdgpu_bo_free(bo); + loop++; + } + /* clean resources */ + free(resources); + free(ibs_request); + free(ib_info); + free(pm4); + + /* end of test */ + r = amdgpu_cs_ctx_free(device_handle, context_handle); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_command_submission_sdma_const_fill(void) +{ + const int sdma_write_length = 1024 * 1024; + const int pm4_dw = 256; + amdgpu_context_handle context_handle; + amdgpu_bo_handle bo; + amdgpu_bo_handle *resources; + uint32_t *pm4; + struct amdgpu_cs_ib_info *ib_info; + struct amdgpu_cs_request *ibs_request; + uint64_t bo_mc; + volatile uint32_t *bo_cpu; + int i, j, r, loop; + uint64_t gtt_flags[3] = {0, AMDGPU_GEM_CREATE_CPU_GTT_UC, + AMDGPU_GEM_CREATE_CPU_GTT_WC}; + + pm4 = malloc(pm4_dw * 4); + CU_ASSERT_NOT_EQUAL(pm4, NULL); + + ib_info = malloc(sizeof(*ib_info)); + CU_ASSERT_NOT_EQUAL(ib_info, NULL); + memset(ib_info, 0, sizeof(*ib_info)); + + ibs_request = malloc(sizeof(*ibs_request)); + CU_ASSERT_NOT_EQUAL(ibs_request, NULL); + memset(ibs_request, 0, sizeof(*ibs_request)); + + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + CU_ASSERT_EQUAL(r, 0); + + /* prepare resource */ + resources = malloc(sizeof(amdgpu_bo_handle)); + CU_ASSERT_NOT_EQUAL(resources, NULL); + + loop = 0; + while(loop < 3) { + /* allocate UC bo for sDMA use */ + bo = gpu_mem_alloc(device_handle, + sdma_write_length, 4096, + AMDGPU_GEM_DOMAIN_GTT, + gtt_flags[loop], &bo_mc); + + CU_ASSERT_EQUAL(amdgpu_bo_cpu_map(bo, (void **)&bo_cpu), 0); + CU_ASSERT_NOT_EQUAL(bo_cpu, NULL); + + /* clear bo */ + memset((void*)bo_cpu, 0, sdma_write_length); + + resources[0] = bo; + + /* fullfill PM4: test DMA const fill */ + i = j = 0; + pm4[i++] = SDMA_PACKET(SDMA_OPCODE_CONSTANT_FILL, 0, + SDMA_CONSTANT_FILL_EXTRA_SIZE(2)); + pm4[i++] = 0xffffffff & bo_mc; + pm4[i++] = (0xffffffff00000000 & bo_mc) >> 32; + pm4[i++] = 0xdeadbeaf; + pm4[i++] = sdma_write_length; + + amdgpu_sdma_test_exec_cs(context_handle, 0, + i, pm4, + 1, resources, + ib_info, ibs_request); + + /* verify if SDMA test result meets with expected */ + i = 0; + while(i < (sdma_write_length / 4)) { + CU_ASSERT_EQUAL(bo_cpu[i++], 0xdeadbeaf); + } + amdgpu_bo_free(bo); + loop++; + } + /* clean resources */ + free(resources); + free(ibs_request); + free(ib_info); + free(pm4); + + /* end of test */ + r = amdgpu_cs_ctx_free(device_handle, context_handle); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_command_submission_sdma_copy_linear(void) +{ + const int sdma_write_length = 1024; + const int pm4_dw = 256; + amdgpu_context_handle context_handle; + amdgpu_bo_handle bo1, bo2; + amdgpu_bo_handle *resources; + uint32_t *pm4; + struct amdgpu_cs_ib_info *ib_info; + struct amdgpu_cs_request *ibs_request; + uint64_t bo1_mc, bo2_mc; + volatile unsigned char *bo1_cpu, *bo2_cpu; + int i, j, r, loop1, loop2; + uint64_t gtt_flags[3] = {0, AMDGPU_GEM_CREATE_CPU_GTT_UC, + AMDGPU_GEM_CREATE_CPU_GTT_WC}; + + pm4 = malloc(pm4_dw * 4); + CU_ASSERT_NOT_EQUAL(pm4, NULL); + + ib_info = malloc(sizeof(*ib_info)); + CU_ASSERT_NOT_EQUAL(ib_info, NULL); + memset(ib_info, 0, sizeof(*ib_info)); + + ibs_request = malloc(sizeof(*ibs_request)); + CU_ASSERT_NOT_EQUAL(ibs_request, NULL); + memset(ibs_request, 0, sizeof(*ibs_request)); + + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + CU_ASSERT_EQUAL(r, 0); + + /* prepare resource */ + resources = malloc(2 * sizeof(amdgpu_bo_handle)); + CU_ASSERT_NOT_EQUAL(resources, NULL); + + loop1 = loop2 = 0; + /* run 9 circle to test all mapping combination */ + while(loop1 < 3) { + while(loop2 < 3) { + /* allocate UC bo1for sDMA use */ + bo1 = gpu_mem_alloc(device_handle, + sdma_write_length, 4096, + AMDGPU_GEM_DOMAIN_GTT, + gtt_flags[loop1], &bo1_mc); + + CU_ASSERT_EQUAL(amdgpu_bo_cpu_map(bo1, (void **)&bo1_cpu), 0); + CU_ASSERT_NOT_EQUAL(bo1_cpu, NULL); + + /* set bo1 */ + memset((void*)bo1_cpu, 0xaa, sdma_write_length); + + /* allocate UC bo2 for sDMA use */ + bo2 = gpu_mem_alloc(device_handle, + sdma_write_length, 4096, + AMDGPU_GEM_DOMAIN_GTT, + gtt_flags[loop2], &bo2_mc); + + CU_ASSERT_EQUAL(amdgpu_bo_cpu_map(bo2, (void **)&bo2_cpu), 0); + CU_ASSERT_NOT_EQUAL(bo2_cpu, NULL); + + /* clear bo2 */ + memset((void*)bo2_cpu, 0, sdma_write_length); + + resources[0] = bo1; + resources[1] = bo2; + + /* fullfill PM4: test DMA copy linear */ + i = j = 0; + pm4[i++] = SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_COPY_SUB_OPCODE_LINEAR, 0); + pm4[i++] = sdma_write_length; + pm4[i++] = 0; + pm4[i++] = 0xffffffff & bo1_mc; + pm4[i++] = (0xffffffff00000000 & bo1_mc) >> 32; + pm4[i++] = 0xffffffff & bo2_mc; + pm4[i++] = (0xffffffff00000000 & bo2_mc) >> 32; + + + amdgpu_sdma_test_exec_cs(context_handle, 0, + i, pm4, + 2, resources, + ib_info, ibs_request); + + /* verify if SDMA test result meets with expected */ + i = 0; + while(i < sdma_write_length) { + CU_ASSERT_EQUAL(bo2_cpu[i++], 0xaa); + } + amdgpu_bo_free(bo1); + amdgpu_bo_free(bo2); + loop2++; + } + loop1++; + } + /* clean resources */ + free(resources); + free(ibs_request); + free(ib_info); + free(pm4); + + /* end of test */ + r = amdgpu_cs_ctx_free(device_handle, context_handle); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_command_submission_sdma(void) +{ + amdgpu_command_submission_sdma_write_linear(); + amdgpu_command_submission_sdma_const_fill(); + amdgpu_command_submission_sdma_copy_linear(); +} + +static void amdgpu_userptr_test(void) +{ + int i, r, j; + uint32_t *pm4 = NULL; + uint64_t bo_mc; + void *ptr = NULL; + int pm4_dw = 256; + int sdma_write_length = 4; + amdgpu_bo_handle handle; + amdgpu_context_handle context_handle; + struct amdgpu_cs_ib_info *ib_info; + struct amdgpu_cs_request *ibs_request; + struct amdgpu_bo_alloc_result res; + + memset(&res, 0, sizeof(res)); + + pm4 = malloc(pm4_dw * sizeof(*pm4)); + CU_ASSERT_NOT_EQUAL(pm4, NULL); + + ib_info = malloc(sizeof(*ib_info)); + CU_ASSERT_NOT_EQUAL(ib_info, NULL); + + memset(ib_info, 0, sizeof(*ib_info)); + ibs_request = malloc(sizeof(*ibs_request)); + CU_ASSERT_NOT_EQUAL(ibs_request, NULL); + + memset(ibs_request, 0, sizeof(*ibs_request)); + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + CU_ASSERT_EQUAL(r, 0); + + ptr = memalign(BUFFER_SIZE); + CU_ASSERT_NOT_EQUAL(ptr, NULL); + + r = amdgpu_create_bo_from_user_mem(device_handle, + ptr, BUFFER_SIZE, &res); + CU_ASSERT_EQUAL(r, 0); + bo_mc = res.virtual_mc_base_address; + handle = res.buf_handle; + + j = i = 0; + pm4[i++] = SDMA_PACKET(SDMA_OPCODE_WRITE, + SDMA_WRITE_SUB_OPCODE_LINEAR, 0); + pm4[i++] = 0xffffffff & bo_mc; + pm4[i++] = (0xffffffff00000000 & bo_mc) >> 32; + pm4[i++] = sdma_write_length; + + while (j++ < sdma_write_length) + pm4[i++] = 0xdeadbeaf; + + amdgpu_sdma_test_exec_cs(context_handle, 0, + i, pm4, + 1, &handle, + ib_info, ibs_request); + i = 0; + while (i < sdma_write_length) { + CU_ASSERT_EQUAL(((int*)ptr)[i++], 0xdeadbeaf); + } + free(ibs_request); + free(ib_info); + free(pm4); + r = amdgpu_bo_free(res.buf_handle); + CU_ASSERT_EQUAL(r, 0); + free(ptr); + + r = amdgpu_cs_ctx_free(device_handle, context_handle); + CU_ASSERT_EQUAL(r, 0); +} diff --git a/tests/amdgpu/bo_tests.c b/tests/amdgpu/bo_tests.c new file mode 100644 index 0000000..73701b7 --- /dev/null +++ b/tests/amdgpu/bo_tests.c @@ -0,0 +1,151 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <stdio.h> + +#include "CUnit/Basic.h" + +#include "amdgpu_test.h" +#include "amdgpu_drm.h" + +#define BUFFER_SIZE (4*1024) +#define BUFFER_ALIGN (4*1024) + +static amdgpu_device_handle device_handle; +static uint32_t major_version; +static uint32_t minor_version; + +static amdgpu_bo_handle buffer_handle; +static uint64_t virtual_mc_base_address; + +static void amdgpu_bo_export_import(void); +static void amdgpu_bo_metadata(void); +static void amdgpu_bo_map_unmap(void); + +CU_TestInfo bo_tests[] = { + { "Export/Import", amdgpu_bo_export_import }, +#if 0 + { "Metadata", amdgpu_bo_metadata }, +#endif + { "CPU map/unmap", amdgpu_bo_map_unmap }, + CU_TEST_INFO_NULL, +}; + +int suite_bo_tests_init(void) +{ + struct amdgpu_bo_alloc_request req = {0}; + struct amdgpu_bo_alloc_result res = {0}; + int r; + + r = amdgpu_device_initialize(drm_amdgpu[0], &major_version, + &minor_version, &device_handle); + if (r) + return CUE_SINIT_FAILED; + + req.alloc_size = BUFFER_SIZE; + req.phys_alignment = BUFFER_ALIGN; + req.preferred_heap = AMDGPU_GEM_DOMAIN_GTT; + + r = amdgpu_bo_alloc(device_handle, &req, &res); + if (r) + return CUE_SINIT_FAILED; + + buffer_handle = res.buf_handle; + virtual_mc_base_address = res.virtual_mc_base_address; + + return CUE_SUCCESS; +} + +int suite_bo_tests_clean(void) +{ + int r; + + r = amdgpu_bo_free(buffer_handle); + if (r) + return CUE_SCLEAN_FAILED; + + r = amdgpu_device_deinitialize(device_handle); + if (r) + return CUE_SCLEAN_FAILED; + + return CUE_SUCCESS; +} + +static void amdgpu_bo_export_import_do_type(enum amdgpu_bo_handle_type type) +{ + struct amdgpu_bo_import_result res = {0}; + uint32_t shared_handle; + int r; + + r = amdgpu_bo_export(buffer_handle, type, &shared_handle); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_import(device_handle, type, shared_handle, &res); + CU_ASSERT_EQUAL(r, 0); + + CU_ASSERT_EQUAL(res.buf_handle, buffer_handle); + CU_ASSERT_EQUAL(res.alloc_size, BUFFER_SIZE); + + r = amdgpu_bo_free(res.buf_handle); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_bo_export_import(void) +{ + amdgpu_bo_export_import_do_type(amdgpu_bo_handle_type_gem_flink_name); + amdgpu_bo_export_import_do_type(amdgpu_bo_handle_type_dma_buf_fd); +} + +static void amdgpu_bo_metadata(void) +{ + struct amdgpu_bo_metadata meta = {0}; + struct amdgpu_bo_info info = {0}; + int r; + + meta.size_metadata = 1; + meta.umd_metadata[0] = 0xdeadbeef; + + r = amdgpu_bo_set_metadata(buffer_handle, &meta); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_query_info(buffer_handle, &info); + CU_ASSERT_EQUAL(r, 0); + + CU_ASSERT_EQUAL(info.metadata.size_metadata, 1); + CU_ASSERT_EQUAL(info.metadata.umd_metadata[0], 0xdeadbeef); +} + +static void amdgpu_bo_map_unmap(void) +{ + uint32_t *ptr; + int i, r; + + r = amdgpu_bo_cpu_map(buffer_handle, (void **)&ptr); + CU_ASSERT_EQUAL(r, 0); + CU_ASSERT_NOT_EQUAL(ptr, NULL); + + for (i = 0; i < (BUFFER_SIZE / 4); ++i) + ptr[i] = 0xdeadbeef; + + r = amdgpu_bo_cpu_unmap(buffer_handle); + CU_ASSERT_EQUAL(r, 0); +} diff --git a/tests/amdgpu/cs_tests.c b/tests/amdgpu/cs_tests.c new file mode 100644 index 0000000..ca63ae2 --- /dev/null +++ b/tests/amdgpu/cs_tests.c @@ -0,0 +1,319 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 <stdio.h> + +#include "CUnit/Basic.h" + +#include "util_math.h" + +#include "amdgpu_test.h" +#include "uvd_messages.h" +#include "amdgpu_drm.h" +#include "amdgpu_internal.h" + +#define IB_SIZE amdgpu_cs_ib_size_4K +#define MAX_RESOURCES 16 + +static amdgpu_device_handle device_handle; +static uint32_t major_version; +static uint32_t minor_version; +static uint32_t family_id; + +static amdgpu_context_handle context_handle; +static amdgpu_ib_handle ib_handle; +uint32_t *ib_cpu; + +static amdgpu_bo_handle resources[MAX_RESOURCES]; +static unsigned num_resources; + +static void amdgpu_cs_uvd_create(void); +static void amdgpu_cs_uvd_decode(void); +static void amdgpu_cs_uvd_destroy(void); + +CU_TestInfo cs_tests[] = { + { "UVD create", amdgpu_cs_uvd_create }, + { "UVD decode", amdgpu_cs_uvd_decode }, + { "UVD destroy", amdgpu_cs_uvd_destroy }, + CU_TEST_INFO_NULL, +}; + +int suite_cs_tests_init(void) +{ + struct amdgpu_cs_ib_alloc_result ib_result = {0}; + int r; + + r = amdgpu_device_initialize(drm_amdgpu[0], &major_version, + &minor_version, &device_handle); + if (r) + return CUE_SINIT_FAILED; + + family_id = device_handle->info.family_id; + + r = amdgpu_cs_ctx_create(device_handle, &context_handle); + if (r) + return CUE_SINIT_FAILED; + + r = amdgpu_cs_alloc_ib(device_handle, context_handle, + IB_SIZE, &ib_result); + if (r) + return CUE_SINIT_FAILED; + + ib_handle = ib_result.handle; + ib_cpu = ib_result.cpu; + + return CUE_SUCCESS; +} + +int suite_cs_tests_clean(void) +{ + int r; + + r = amdgpu_cs_free_ib(device_handle, context_handle, ib_handle); + if (r) + return CUE_SCLEAN_FAILED; + + r = amdgpu_cs_ctx_free(device_handle, context_handle); + if (r) + return CUE_SCLEAN_FAILED; + + r = amdgpu_device_deinitialize(device_handle); + if (r) + return CUE_SCLEAN_FAILED; + + return CUE_SUCCESS; +} + +static int submit(unsigned ndw, unsigned ip) +{ + struct amdgpu_cs_ib_alloc_result ib_result = {0}; + struct amdgpu_cs_request ibs_request = {0}; + struct amdgpu_cs_ib_info ib_info = {0}; + struct amdgpu_cs_query_fence fence_status = {0}; + uint32_t expired; + int r; + + ib_info.ib_handle = ib_handle; + ib_info.size = ndw; + + ibs_request.ip_type = ip; + ibs_request.number_of_resources = num_resources; + ibs_request.resources = resources; + ibs_request.number_of_ibs = 1; + ibs_request.ibs = &ib_info; + + r = amdgpu_cs_submit(device_handle, context_handle, 0, + &ibs_request, 1, &fence_status.fence); + if (r) + return r; + + r = amdgpu_cs_alloc_ib(device_handle, context_handle, + IB_SIZE, &ib_result); + if (r) + return r; + + ib_handle = ib_result.handle; + ib_cpu = ib_result.cpu; + + fence_status.context = context_handle; + fence_status.timeout_ns = AMDGPU_TIMEOUT_INFINITE; + fence_status.ip_type = ip; + + r = amdgpu_cs_query_fence_status(device_handle, &fence_status, &expired); + if (r) + return r; + + return 0; +} + +static void uvd_cmd(uint64_t addr, unsigned cmd, int *idx) +{ + ib_cpu[(*idx)++] = 0x3BC4; + ib_cpu[(*idx)++] = addr; + ib_cpu[(*idx)++] = 0x3BC5; + ib_cpu[(*idx)++] = addr >> 32; + ib_cpu[(*idx)++] = 0x3BC3; + ib_cpu[(*idx)++] = cmd << 1; +} + +static void amdgpu_cs_uvd_create(void) +{ + struct amdgpu_bo_alloc_request req = {0}; + struct amdgpu_bo_alloc_result res = {0}; + void *msg; + int i, r; + + req.alloc_size = 4*1024; + req.preferred_heap = AMDGPU_GEM_DOMAIN_GTT; + + r = amdgpu_bo_alloc(device_handle, &req, &res); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_cpu_map(res.buf_handle, &msg); + CU_ASSERT_EQUAL(r, 0); + + memcpy(msg, uvd_create_msg, sizeof(uvd_create_msg)); + if (family_id >= AMDGPU_FAMILY_VI) + ((uint8_t*)msg)[0x10] = 7; + + r = amdgpu_bo_cpu_unmap(res.buf_handle); + CU_ASSERT_EQUAL(r, 0); + + num_resources = 0; + resources[num_resources++] = res.buf_handle; + + i = 0; + uvd_cmd(res.virtual_mc_base_address, 0x0, &i); + for (; i % 16; ++i) + ib_cpu[i] = 0x80000000; + + r = submit(i, AMDGPU_HW_IP_UVD); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_free(resources[0]); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_cs_uvd_decode(void) +{ + const unsigned dpb_size = 15923584, dt_size = 737280; + uint64_t msg_addr, fb_addr, bs_addr, dpb_addr, dt_addr, it_addr; + struct amdgpu_bo_alloc_request req = {0}; + struct amdgpu_bo_alloc_result res = {0}; + uint64_t sum; + uint8_t *ptr; + int i, r; + + req.alloc_size = 4*1024; /* msg */ + req.alloc_size += 4*1024; /* fb */ + if (family_id >= AMDGPU_FAMILY_VI) + req.alloc_size += 4096; /*it_scaling_table*/ + req.alloc_size += ALIGN(sizeof(uvd_bitstream), 4*1024); + req.alloc_size += ALIGN(dpb_size, 4*1024); + req.alloc_size += ALIGN(dt_size, 4*1024); + + req.preferred_heap = AMDGPU_GEM_DOMAIN_GTT; + + r = amdgpu_bo_alloc(device_handle, &req, &res); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_cpu_map(res.buf_handle, (void **)&ptr); + CU_ASSERT_EQUAL(r, 0); + + memcpy(ptr, uvd_decode_msg, sizeof(uvd_create_msg)); + if (family_id >= AMDGPU_FAMILY_VI) + ptr[0x10] = 7; + + ptr += 4*1024; + memset(ptr, 0, 4*1024); + if (family_id >= AMDGPU_FAMILY_VI) { + ptr += 4*1024; + memcpy(ptr, uvd_it_scaling_table, sizeof(uvd_it_scaling_table)); + } + + ptr += 4*1024; + memcpy(ptr, uvd_bitstream, sizeof(uvd_bitstream)); + + ptr += ALIGN(sizeof(uvd_bitstream), 4*1024); + memset(ptr, 0, dpb_size); + + ptr += ALIGN(dpb_size, 4*1024); + memset(ptr, 0, dt_size); + + num_resources = 0; + resources[num_resources++] = res.buf_handle; + + msg_addr = res.virtual_mc_base_address; + fb_addr = msg_addr + 4*1024; + if (family_id >= AMDGPU_FAMILY_VI) { + it_addr = fb_addr + 4*1024; + bs_addr = it_addr + 4*1024; + } else + bs_addr = fb_addr + 4*1024; + dpb_addr = ALIGN(bs_addr + sizeof(uvd_bitstream), 4*1024); + dt_addr = ALIGN(dpb_addr + dpb_size, 4*1024); + + i = 0; + uvd_cmd(msg_addr, 0x0, &i); + uvd_cmd(dpb_addr, 0x1, &i); + uvd_cmd(dt_addr, 0x2, &i); + uvd_cmd(fb_addr, 0x3, &i); + uvd_cmd(bs_addr, 0x100, &i); + if (family_id >= AMDGPU_FAMILY_VI) + uvd_cmd(it_addr, 0x204, &i); + ib_cpu[i++] = 0x3BC6; + ib_cpu[i++] = 0x1; + for (; i % 16; ++i) + ib_cpu[i] = 0x80000000; + + r = submit(i, AMDGPU_HW_IP_UVD); + CU_ASSERT_EQUAL(r, 0); + + /* TODO: use a real CRC32 */ + for (i = 0, sum = 0; i < dt_size; ++i) + sum += ptr[i]; + CU_ASSERT_EQUAL(sum, 0x20345d8); + + r = amdgpu_bo_cpu_unmap(res.buf_handle); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_free(resources[0]); + CU_ASSERT_EQUAL(r, 0); +} + +static void amdgpu_cs_uvd_destroy(void) +{ + struct amdgpu_bo_alloc_request req = {0}; + struct amdgpu_bo_alloc_result res = {0}; + void *msg; + int i, r; + + req.alloc_size = 4*1024; + req.preferred_heap = AMDGPU_GEM_DOMAIN_GTT; + + r = amdgpu_bo_alloc(device_handle, &req, &res); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_cpu_map(res.buf_handle, &msg); + CU_ASSERT_EQUAL(r, 0); + + memcpy(msg, uvd_destroy_msg, sizeof(uvd_create_msg)); + if (family_id >= AMDGPU_FAMILY_VI) + ((uint8_t*)msg)[0x10] = 7; + + r = amdgpu_bo_cpu_unmap(res.buf_handle); + CU_ASSERT_EQUAL(r, 0); + + num_resources = 0; + resources[num_resources++] = res.buf_handle; + + i = 0; + uvd_cmd(res.virtual_mc_base_address, 0x0, &i); + for (; i % 16; ++i) + ib_cpu[i] = 0x80000000; + + r = submit(i, AMDGPU_HW_IP_UVD); + CU_ASSERT_EQUAL(r, 0); + + r = amdgpu_bo_free(resources[0]); + CU_ASSERT_EQUAL(r, 0); +} diff --git a/tests/amdgpu/uvd_messages.h b/tests/amdgpu/uvd_messages.h new file mode 100644 index 0000000..d4069c9 --- /dev/null +++ b/tests/amdgpu/uvd_messages.h @@ -0,0 +1,813 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _uvd_messages_h_ +#define _uvd_messages_h_ + +static uint8_t uvd_create_msg[] = { + 0xe4,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x44,0x40,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x03,0x00,0x00, + 0xe0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xf9,0xf2,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static uint8_t uvd_bitstream[] ={ + 0x00,0x00,0x01,0x25,0xb8,0x20,0x20,0x21,0x44,0xc5,0x00,0x01,0x57,0x9b,0xef,0xbe, + 0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb, + 0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef, + 0xbe,0xfb,0xef,0xbe,0xff,0x87,0xff,0xc2,0x58,0x0e,0x00,0x02,0x02,0xa0,0x00,0x20, + 0x3a,0x00,0x0d,0x00,0x01,0x01,0xa4,0xcb,0x94,0x73,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xaf,0x00,0x00,0x01,0x25,0x00,0xa2,0xb8,0x20,0x20,0x21,0x44, + 0xc5,0x00,0x01,0x57,0x9b,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe, + 0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb, + 0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xff,0x87,0xff,0xc2,0x58, + 0x0e,0x00,0x02,0x02,0xa0,0x00,0x20,0x3a,0x00,0x0d,0x00,0x01,0x01,0xa4,0xcb,0x94, + 0x73,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xaf,0x00,0x00,0x01,0x25, + 0x00,0x51,0x2e,0x08,0x08,0x08,0x51,0x31,0x40,0x00,0x55,0xe6,0xfb,0xef,0xbe,0xfb, + 0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef, + 0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe, + 0xfb,0xef,0xbf,0xe1,0xff,0xf0,0x96,0x03,0x80,0x00,0x80,0xa8,0x00,0x08,0x0e,0x80, + 0x03,0x40,0x00,0x40,0x69,0x32,0xe5,0x1c,0xfa,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xc0,0x00,0x00,0x01,0x25,0x00,0x79,0xae,0x08,0x08,0x08,0x51,0x31, + 0x40,0x00,0x55,0xe6,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe, + 0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb, + 0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbf,0xe1,0xff,0xf0,0x96,0x03, + 0x80,0x00,0x80,0xa8,0x00,0x08,0x0e,0x80,0x03,0x40,0x00,0x40,0x69,0x32,0xe5,0x1c, + 0xfa,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xc0,0x00,0x00,0x01,0x25, + 0x00,0x28,0x8b,0x82,0x02,0x02,0x14,0x4c,0x50,0x00,0x15,0x79,0xbe,0xfb,0xef,0xbe, + 0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb, + 0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef,0xbe,0xfb,0xef, + 0xbe,0xfb,0xef,0xf8,0x7f,0xfc,0x25,0x80,0xe0,0x00,0x20,0x2a,0x00,0x02,0x03,0xa0, + 0x00,0xd0,0x00,0x10,0x1a,0x4c,0xb9,0x47,0x3e,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb, + 0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae, + 0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba,0xeb,0xae,0xba, + 0xeb,0xae,0xba,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static uint8_t uvd_decode_msg[] = { + 0xe4,0x0d,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x44,0x40,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x03,0x00,0x00,0xe0,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x80,0xf9,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x05,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xc0,0x03,0x00,0x00,0x80,0x07,0x00,0x00,0x60,0x09,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x01,0x00,0x00,0x01,0x00,0x03,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static uint8_t uvd_destroy_msg[] = { + 0xe4,0x0d,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x44,0x40,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +static uint8_t uvd_it_scaling_table[] = { + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, +}; + +#endif /* _uvd_messages_h_ */ diff --git a/tests/kmstest/main.c b/tests/kmstest/main.c index 7d50f5b..f39364f 100644 --- a/tests/kmstest/main.c +++ b/tests/kmstest/main.c @@ -62,6 +62,7 @@ static const char *drivers[] = { "nouveau", "vmwgfx", "exynos", + "amdgpu", "imx-drm", "rockchip", NULL
Hi Alex,
On 24 April 2015 at 16:13, Alex Deucher alexdeucher@gmail.com wrote:
This adds some basic unit tests for the new amdgpu driver.
v2: use common util_math.h
Can I put forward a few suggestions: - Can we use calloc over malloc. It will likely save you/others some headaches in the future. - Use capital letters for header guards - Annotate the CU_SuiteInfo/CU_TestInfo as static.
...
+amdgpu_test_LDFLAGS = $(CUNIT_LIBS)
LDFLAGS should not be used for LIBS (normally) - please fold it with LDADD above;
amdgpu_test_LDADD = \ $(top_builddir)/libdrm.la \ $(top_builddir)/amdgpu/libdrm_amdgpu.la \ $(CUNIT_LIBS)
Please add the headers in the list. amdgpu_test.h uvd_messages.h
+int drm_amdgpu[MAX_CARDS_SUPPORTED];
We're using only a single one in the tests. Why the array ?
+int main(int argc, char **argv) +{
0 is a valid fd number.
1 is a valid fd number. Perhaps < 0 ?
Please use drmGetVersion/drmFreeVersion rather than open coding it. This implementation leaks memory.
Swap CUE_SUCCESS and CU_initialize_registry() ?
Close the fd before return/exit(). The rest of the could use it as well.
Not familiar with CUnit, but looks like we should use CU_cleanup_registry() here. Same for the rest of the file ?
Unused ?
+static void amdgpu_command_submission_sdma_const_fill(void) +{
pm4 = calloc(pm4_dw, sizeof(*pm4));
Bikeshed: Most of the patch uses for loops
+}
+static void amdgpu_command_submission_sdma_copy_linear(void) +{
Same suggestions as amdgpu_command_submission_sdma_const_fill
Cheers Emil
On 24/04/15 15:13, Alex Deucher wrote:
It does have one advantage though - it's typeless. Thus it allows nouveau and intel to have their custom list implementations. For example - struct nouveau_list (plain double list) - struct {mem_,}block (double list with some extra bookkeeping)
This change might cause small issues with Jerome's bof replay tool (joujou), although I should have it merged with libdrm as this lands (or shortly after).
With the comments sent out earlier for 3/4 (v4) and 4/4(v2), feel free to add Reviewed-by: Emil Velikov emil.l.velikov@gmail.com, fwiw.
Thanks Emil
dri-devel@lists.freedesktop.org