diff options
Diffstat (limited to '')
-rw-r--r-- | radeon/Android.mk | 14 | ||||
-rw-r--r-- | radeon/Makefile.sources | 21 | ||||
-rw-r--r-- | radeon/bof.c | 477 | ||||
-rw-r--r-- | radeon/bof.h | 90 | ||||
-rw-r--r-- | radeon/libdrm_radeon.pc.in | 11 | ||||
-rw-r--r-- | radeon/meson.build | 70 | ||||
-rw-r--r-- | radeon/r600_pci_ids.h | 487 | ||||
-rw-r--r-- | radeon/radeon-symbols.txt | 44 | ||||
-rw-r--r-- | radeon/radeon_bo.c | 142 | ||||
-rw-r--r-- | radeon/radeon_bo.h | 73 | ||||
-rw-r--r-- | radeon/radeon_bo_gem.c | 399 | ||||
-rw-r--r-- | radeon/radeon_bo_gem.h | 48 | ||||
-rw-r--r-- | radeon/radeon_bo_int.h | 45 | ||||
-rw-r--r-- | radeon/radeon_cs.c | 95 | ||||
-rw-r--r-- | radeon/radeon_cs.h | 141 | ||||
-rw-r--r-- | radeon/radeon_cs_gem.c | 556 | ||||
-rw-r--r-- | radeon/radeon_cs_gem.h | 41 | ||||
-rw-r--r-- | radeon/radeon_cs_int.h | 67 | ||||
-rw-r--r-- | radeon/radeon_cs_space.c | 248 | ||||
-rw-r--r-- | radeon/radeon_surface.c | 2567 | ||||
-rw-r--r-- | radeon/radeon_surface.h | 149 |
21 files changed, 5785 insertions, 0 deletions
diff --git a/radeon/Android.mk b/radeon/Android.mk new file mode 100644 index 0000000..71040da --- /dev/null +++ b/radeon/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# Import variables LIBDRM_RADEON_FILES, LIBDRM_RADEON_H_FILES +include $(LOCAL_PATH)/Makefile.sources + +LOCAL_MODULE := libdrm_radeon + +LOCAL_SHARED_LIBRARIES := libdrm + +LOCAL_SRC_FILES := $(LIBDRM_RADEON_FILES) + +include $(LIBDRM_COMMON_MK) +include $(BUILD_SHARED_LIBRARY) diff --git a/radeon/Makefile.sources b/radeon/Makefile.sources new file mode 100644 index 0000000..1cf482a --- /dev/null +++ b/radeon/Makefile.sources @@ -0,0 +1,21 @@ +LIBDRM_RADEON_FILES := \ + radeon_bo_gem.c \ + radeon_cs_gem.c \ + radeon_cs_space.c \ + radeon_bo.c \ + radeon_cs.c \ + radeon_surface.c + +LIBDRM_RADEON_H_FILES := \ + radeon_bo.h \ + radeon_cs.h \ + radeon_surface.h \ + radeon_bo_gem.h \ + radeon_cs_gem.h \ + radeon_bo_int.h \ + radeon_cs_int.h \ + r600_pci_ids.h + +LIBDRM_RADEON_BOF_FILES := \ + bof.c \ + bof.h diff --git a/radeon/bof.c b/radeon/bof.c new file mode 100644 index 0000000..0598cc6 --- /dev/null +++ b/radeon/bof.c @@ -0,0 +1,477 @@ +/* + * Copyright 2010 Jerome Glisse <glisse@freedesktop.org> + * + * 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 + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR 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: + * Jerome Glisse + */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "bof.h" + +/* + * helpers + */ +static int bof_entry_grow(bof_t *bof) +{ + bof_t **array; + + if (bof->array_size < bof->nentry) + return 0; + array = realloc(bof->array, (bof->nentry + 16) * sizeof(void*)); + if (array == NULL) + return -ENOMEM; + bof->array = array; + bof->nentry += 16; + return 0; +} + +/* + * object + */ +bof_t *bof_object(void) +{ + bof_t *object; + + object = calloc(1, sizeof(bof_t)); + if (object == NULL) + return NULL; + object->refcount = 1; + object->type = BOF_TYPE_OBJECT; + object->size = 12; + return object; +} + +bof_t *bof_object_get(bof_t *object, const char *keyname) +{ + unsigned i; + + for (i = 0; i < object->array_size; i += 2) { + if (!strcmp(object->array[i]->value, keyname)) { + return object->array[i + 1]; + } + } + return NULL; +} + +int bof_object_set(bof_t *object, const char *keyname, bof_t *value) +{ + bof_t *key; + int r; + + if (object->type != BOF_TYPE_OBJECT) + return -EINVAL; + r = bof_entry_grow(object); + if (r) + return r; + key = bof_string(keyname); + if (key == NULL) + return -ENOMEM; + object->array[object->array_size++] = key; + object->array[object->array_size++] = value; + object->size += value->size; + object->size += key->size; + bof_incref(value); + return 0; +} + +/* + * array + */ +bof_t *bof_array(void) +{ + bof_t *array = bof_object(); + + if (array == NULL) + return NULL; + array->type = BOF_TYPE_ARRAY; + array->size = 12; + return array; +} + +int bof_array_append(bof_t *array, bof_t *value) +{ + int r; + if (array->type != BOF_TYPE_ARRAY) + return -EINVAL; + r = bof_entry_grow(array); + if (r) + return r; + array->array[array->array_size++] = value; + array->size += value->size; + bof_incref(value); + return 0; +} + +bof_t *bof_array_get(bof_t *bof, unsigned i) +{ + if (!bof_is_array(bof) || i >= bof->array_size) + return NULL; + return bof->array[i]; +} + +unsigned bof_array_size(bof_t *bof) +{ + if (!bof_is_array(bof)) + return 0; + return bof->array_size; +} + +/* + * blob + */ +bof_t *bof_blob(unsigned size, void *value) +{ + bof_t *blob = bof_object(); + + if (blob == NULL) + return NULL; + blob->type = BOF_TYPE_BLOB; + blob->value = calloc(1, size); + if (blob->value == NULL) { + bof_decref(blob); + return NULL; + } + blob->size = size; + memcpy(blob->value, value, size); + blob->size += 12; + return blob; +} + +unsigned bof_blob_size(bof_t *bof) +{ + if (!bof_is_blob(bof)) + return 0; + return bof->size - 12; +} + +void *bof_blob_value(bof_t *bof) +{ + if (!bof_is_blob(bof)) + return NULL; + return bof->value; +} + +/* + * string + */ +bof_t *bof_string(const char *value) +{ + bof_t *string = bof_object(); + + if (string == NULL) + return NULL; + string->type = BOF_TYPE_STRING; + string->size = strlen(value) + 1; + string->value = calloc(1, string->size); + if (string->value == NULL) { + bof_decref(string); + return NULL; + } + strcpy(string->value, value); + string->size += 12; + return string; +} + +/* + * int32 + */ +bof_t *bof_int32(int32_t value) +{ + bof_t *int32 = bof_object(); + + if (int32 == NULL) + return NULL; + int32->type = BOF_TYPE_INT32; + int32->size = 4; + int32->value = calloc(1, int32->size); + if (int32->value == NULL) { + bof_decref(int32); + return NULL; + } + memcpy(int32->value, &value, 4); + int32->size += 12; + return int32; +} + +int32_t bof_int32_value(bof_t *bof) +{ + return *((uint32_t*)bof->value); +} + +/* + * common + */ +static void bof_indent(int level) +{ + int i; + + for (i = 0; i < level; i++) + fprintf(stderr, " "); +} + +static void bof_print_bof(bof_t *bof, int level, int entry) +{ + bof_indent(level); + if (bof == NULL) { + fprintf(stderr, "--NULL-- for entry %d\n", entry); + return; + } + switch (bof->type) { + case BOF_TYPE_STRING: + fprintf(stderr, "%p string [%s %d]\n", bof, (char*)bof->value, bof->size); + break; + case BOF_TYPE_INT32: + fprintf(stderr, "%p int32 [%d %d]\n", bof, *(int*)bof->value, bof->size); + break; + case BOF_TYPE_BLOB: + fprintf(stderr, "%p blob [%d]\n", bof, bof->size); + break; + case BOF_TYPE_NULL: + fprintf(stderr, "%p null [%d]\n", bof, bof->size); + break; + case BOF_TYPE_OBJECT: + fprintf(stderr, "%p object [%d %d]\n", bof, bof->array_size / 2, bof->size); + break; + case BOF_TYPE_ARRAY: + fprintf(stderr, "%p array [%d %d]\n", bof, bof->array_size, bof->size); + break; + default: + fprintf(stderr, "%p unknown [%d]\n", bof, bof->type); + return; + } +} + +static void bof_print_rec(bof_t *bof, int level, int entry) +{ + unsigned i; + + bof_print_bof(bof, level, entry); + for (i = 0; i < bof->array_size; i++) { + bof_print_rec(bof->array[i], level + 2, i); + } +} + +void bof_print(bof_t *bof) +{ + bof_print_rec(bof, 0, 0); +} + +static int bof_read(bof_t *root, FILE *file, long end, int level) +{ + bof_t *bof = NULL; + int r; + + if (ftell(file) >= end) { + return 0; + } + r = bof_entry_grow(root); + if (r) + return r; + bof = bof_object(); + if (bof == NULL) + return -ENOMEM; + bof->offset = ftell(file); + r = fread(&bof->type, 4, 1, file); + if (r != 1) + goto out_err; + r = fread(&bof->size, 4, 1, file); + if (r != 1) + goto out_err; + r = fread(&bof->array_size, 4, 1, file); + if (r != 1) + goto out_err; + switch (bof->type) { + case BOF_TYPE_STRING: + case BOF_TYPE_INT32: + case BOF_TYPE_BLOB: + bof->value = calloc(1, bof->size - 12); + if (bof->value == NULL) { + goto out_err; + } + r = fread(bof->value, bof->size - 12, 1, file); + if (r != 1) { + fprintf(stderr, "error reading %d\n", bof->size - 12); + goto out_err; + } + break; + case BOF_TYPE_NULL: + return 0; + case BOF_TYPE_OBJECT: + case BOF_TYPE_ARRAY: + r = bof_read(bof, file, bof->offset + bof->size, level + 2); + if (r) + goto out_err; + break; + default: + fprintf(stderr, "invalid type %d\n", bof->type); + goto out_err; + } + root->array[root->centry++] = bof; + return bof_read(root, file, end, level); +out_err: + bof_decref(bof); + return -EINVAL; +} + +bof_t *bof_load_file(const char *filename) +{ + bof_t *root = bof_object(); + int r; + + if (root == NULL) { + fprintf(stderr, "%s failed to create root object\n", __func__); + return NULL; + } + root->file = fopen(filename, "r"); + if (root->file == NULL) + goto out_err; + r = fseek(root->file, 0L, SEEK_SET); + if (r) { + fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename); + goto out_err; + } + root->offset = ftell(root->file); + r = fread(&root->type, 4, 1, root->file); + if (r != 1) + goto out_err; + r = fread(&root->size, 4, 1, root->file); + if (r != 1) + goto out_err; + r = fread(&root->array_size, 4, 1, root->file); + if (r != 1) + goto out_err; + r = bof_read(root, root->file, root->offset + root->size, 2); + if (r) + goto out_err; + return root; +out_err: + bof_decref(root); + return NULL; +} + +void bof_incref(bof_t *bof) +{ + bof->refcount++; +} + +void bof_decref(bof_t *bof) +{ + unsigned i; + + if (bof == NULL) + return; + if (--bof->refcount > 0) + return; + for (i = 0; i < bof->array_size; i++) { + bof_decref(bof->array[i]); + bof->array[i] = NULL; + } + bof->array_size = 0; + if (bof->file) { + fclose(bof->file); + bof->file = NULL; + } + free(bof->array); + free(bof->value); + free(bof); +} + +static int bof_file_write(bof_t *bof, FILE *file) +{ + unsigned i; + int r; + + r = fwrite(&bof->type, 4, 1, file); + if (r != 1) + return -EINVAL; + r = fwrite(&bof->size, 4, 1, file); + if (r != 1) + return -EINVAL; + r = fwrite(&bof->array_size, 4, 1, file); + if (r != 1) + return -EINVAL; + switch (bof->type) { + case BOF_TYPE_NULL: + if (bof->size) + return -EINVAL; + break; + case BOF_TYPE_STRING: + case BOF_TYPE_INT32: + case BOF_TYPE_BLOB: + r = fwrite(bof->value, bof->size - 12, 1, file); + if (r != 1) + return -EINVAL; + break; + case BOF_TYPE_OBJECT: + case BOF_TYPE_ARRAY: + for (i = 0; i < bof->array_size; i++) { + r = bof_file_write(bof->array[i], file); + if (r) + return r; + } + break; + default: + return -EINVAL; + } + return 0; +} + +int bof_dump_file(bof_t *bof, const char *filename) +{ + unsigned i; + int r = 0; + + if (bof->file) { + fclose(bof->file); + bof->file = NULL; + } + bof->file = fopen(filename, "w"); + if (bof->file == NULL) { + fprintf(stderr, "%s failed to open file %s\n", __func__, filename); + r = -EINVAL; + goto out_err; + } + r = fseek(bof->file, 0L, SEEK_SET); + if (r) { + fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename); + goto out_err; + } + r = fwrite(&bof->type, 4, 1, bof->file); + if (r != 1) + goto out_err; + r = fwrite(&bof->size, 4, 1, bof->file); + if (r != 1) + goto out_err; + r = fwrite(&bof->array_size, 4, 1, bof->file); + if (r != 1) + goto out_err; + for (i = 0; i < bof->array_size; i++) { + r = bof_file_write(bof->array[i], bof->file); + if (r) + return r; + } +out_err: + fclose(bof->file); + bof->file = NULL; + return r; +} diff --git a/radeon/bof.h b/radeon/bof.h new file mode 100644 index 0000000..014affb --- /dev/null +++ b/radeon/bof.h @@ -0,0 +1,90 @@ +/* + * Copyright 2010 Jerome Glisse <glisse@freedesktop.org> + * + * 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 + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR 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: + * Jerome Glisse + */ +#ifndef BOF_H +#define BOF_H + +#include <stdio.h> +#include <stdint.h> + +#define BOF_TYPE_STRING 0 +#define BOF_TYPE_NULL 1 +#define BOF_TYPE_BLOB 2 +#define BOF_TYPE_OBJECT 3 +#define BOF_TYPE_ARRAY 4 +#define BOF_TYPE_INT32 5 + +struct bof; + +typedef struct bof { + struct bof **array; + unsigned centry; + unsigned nentry; + unsigned refcount; + FILE *file; + uint32_t type; + uint32_t size; + uint32_t array_size; + void *value; + long offset; +} bof_t; + +extern int bof_file_flush(bof_t *root); +extern bof_t *bof_file_new(const char *filename); +extern int bof_object_dump(bof_t *object, const char *filename); + +/* object */ +extern bof_t *bof_object(void); +extern bof_t *bof_object_get(bof_t *object, const char *keyname); +extern int bof_object_set(bof_t *object, const char *keyname, bof_t *value); +/* array */ +extern bof_t *bof_array(void); +extern int bof_array_append(bof_t *array, bof_t *value); +extern bof_t *bof_array_get(bof_t *bof, unsigned i); +extern unsigned bof_array_size(bof_t *bof); +/* blob */ +extern bof_t *bof_blob(unsigned size, void *value); +extern unsigned bof_blob_size(bof_t *bof); +extern void *bof_blob_value(bof_t *bof); +/* string */ +extern bof_t *bof_string(const char *value); +/* int32 */ +extern bof_t *bof_int32(int32_t value); +extern int32_t bof_int32_value(bof_t *bof); +/* common functions */ +extern void bof_decref(bof_t *bof); +extern void bof_incref(bof_t *bof); +extern bof_t *bof_load_file(const char *filename); +extern int bof_dump_file(bof_t *bof, const char *filename); +extern void bof_print(bof_t *bof); + +static inline int bof_is_object(bof_t *bof){return (bof->type == BOF_TYPE_OBJECT);} +static inline int bof_is_blob(bof_t *bof){return (bof->type == BOF_TYPE_BLOB);} +static inline int bof_is_null(bof_t *bof){return (bof->type == BOF_TYPE_NULL);} +static inline int bof_is_int32(bof_t *bof){return (bof->type == BOF_TYPE_INT32);} +static inline int bof_is_array(bof_t *bof){return (bof->type == BOF_TYPE_ARRAY);} +static inline int bof_is_string(bof_t *bof){return (bof->type == BOF_TYPE_STRING);} + +#endif diff --git a/radeon/libdrm_radeon.pc.in b/radeon/libdrm_radeon.pc.in new file mode 100644 index 0000000..432993a --- /dev/null +++ b/radeon/libdrm_radeon.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libdrm_radeon +Description: Userspace interface to kernel DRM services for radeon +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -ldrm_radeon +Cflags: -I${includedir} -I${includedir}/libdrm +Requires.private: libdrm diff --git a/radeon/meson.build b/radeon/meson.build new file mode 100644 index 0000000..7aa9b70 --- /dev/null +++ b/radeon/meson.build @@ -0,0 +1,70 @@ +# Copyright © 2017-2018 Intel Corporation + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +libdrm_radeon = library( + 'drm_radeon', + [ + files( + 'radeon_bo_gem.c', 'radeon_cs_gem.c', 'radeon_cs_space.c', 'radeon_bo.c', + 'radeon_cs.c', 'radeon_surface.c', + ), + config_file, + ], + c_args : libdrm_c_args, + gnu_symbol_visibility : 'hidden', + include_directories : [inc_root, inc_drm], + link_with : libdrm, + dependencies : [dep_threads, dep_atomic_ops], + version : '1.0.1', + install : true, +) + +ext_libdrm_radeon = declare_dependency( + link_with : [libdrm, libdrm_radeon], + include_directories : [inc_drm, include_directories('.')], +) + +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('libdrm_radeon', ext_libdrm_radeon) +endif + +install_headers( + 'radeon_bo.h', 'radeon_cs.h', 'radeon_surface.h', 'radeon_bo_gem.h', + 'radeon_cs_gem.h', 'radeon_bo_int.h', 'radeon_cs_int.h', 'r600_pci_ids.h', + subdir : 'libdrm' +) + +pkg.generate( + libdrm_radeon, + name : 'libdrm_radeon', + subdirs : ['.', 'libdrm'], + description : 'Userspace interface to kernel DRM services for radeon', +) + +test( + 'radeon-symbols-check', + symbols_check, + args : [ + '--lib', libdrm_radeon, + '--symbols-file', files('radeon-symbols.txt'), + '--nm', prog_nm.full_path(), + ], +) diff --git a/radeon/r600_pci_ids.h b/radeon/r600_pci_ids.h new file mode 100644 index 0000000..a3b2eac --- /dev/null +++ b/radeon/r600_pci_ids.h @@ -0,0 +1,487 @@ +CHIPSET(0x9400, R600_9400, R600) +CHIPSET(0x9401, R600_9401, R600) +CHIPSET(0x9402, R600_9402, R600) +CHIPSET(0x9403, R600_9403, R600) +CHIPSET(0x9405, R600_9405, R600) +CHIPSET(0x940A, R600_940A, R600) +CHIPSET(0x940B, R600_940B, R600) +CHIPSET(0x940F, R600_940F, R600) + +CHIPSET(0x94C0, RV610_94C0, RV610) +CHIPSET(0x94C1, RV610_94C1, RV610) +CHIPSET(0x94C3, RV610_94C3, RV610) +CHIPSET(0x94C4, RV610_94C4, RV610) +CHIPSET(0x94C5, RV610_94C5, RV610) +CHIPSET(0x94C6, RV610_94C6, RV610) +CHIPSET(0x94C7, RV610_94C7, RV610) +CHIPSET(0x94C8, RV610_94C8, RV610) +CHIPSET(0x94C9, RV610_94C9, RV610) +CHIPSET(0x94CB, RV610_94CB, RV610) +CHIPSET(0x94CC, RV610_94CC, RV610) +CHIPSET(0x94CD, RV610_94CD, RV610) + +CHIPSET(0x9580, RV630_9580, RV630) +CHIPSET(0x9581, RV630_9581, RV630) +CHIPSET(0x9583, RV630_9583, RV630) +CHIPSET(0x9586, RV630_9586, RV630) +CHIPSET(0x9587, RV630_9587, RV630) +CHIPSET(0x9588, RV630_9588, RV630) +CHIPSET(0x9589, RV630_9589, RV630) +CHIPSET(0x958A, RV630_958A, RV630) +CHIPSET(0x958B, RV630_958B, RV630) +CHIPSET(0x958C, RV630_958C, RV630) +CHIPSET(0x958D, RV630_958D, RV630) +CHIPSET(0x958E, RV630_958E, RV630) +CHIPSET(0x958F, RV630_958F, RV630) + +CHIPSET(0x9500, RV670_9500, RV670) +CHIPSET(0x9501, RV670_9501, RV670) +CHIPSET(0x9504, RV670_9504, RV670) +CHIPSET(0x9505, RV670_9505, RV670) +CHIPSET(0x9506, RV670_9506, RV670) +CHIPSET(0x9507, RV670_9507, RV670) +CHIPSET(0x9508, RV670_9508, RV670) +CHIPSET(0x9509, RV670_9509, RV670) +CHIPSET(0x950F, RV670_950F, RV670) +CHIPSET(0x9511, RV670_9511, RV670) +CHIPSET(0x9515, RV670_9515, RV670) +CHIPSET(0x9517, RV670_9517, RV670) +CHIPSET(0x9519, RV670_9519, RV670) + +CHIPSET(0x95C0, RV620_95C0, RV620) +CHIPSET(0x95C2, RV620_95C2, RV620) +CHIPSET(0x95C4, RV620_95C4, RV620) +CHIPSET(0x95C5, RV620_95C5, RV620) +CHIPSET(0x95C6, RV620_95C6, RV620) +CHIPSET(0x95C7, RV620_95C7, RV620) +CHIPSET(0x95C9, RV620_95C9, RV620) +CHIPSET(0x95CC, RV620_95CC, RV620) +CHIPSET(0x95CD, RV620_95CD, RV620) +CHIPSET(0x95CE, RV620_95CE, RV620) +CHIPSET(0x95CF, RV620_95CF, RV620) + +CHIPSET(0x9590, RV635_9590, RV635) +CHIPSET(0x9591, RV635_9591, RV635) +CHIPSET(0x9593, RV635_9593, RV635) +CHIPSET(0x9595, RV635_9595, RV635) +CHIPSET(0x9596, RV635_9596, RV635) +CHIPSET(0x9597, RV635_9597, RV635) +CHIPSET(0x9598, RV635_9598, RV635) +CHIPSET(0x9599, RV635_9599, RV635) +CHIPSET(0x959B, RV635_959B, RV635) + +CHIPSET(0x9610, RS780_9610, RS780) +CHIPSET(0x9611, RS780_9611, RS780) +CHIPSET(0x9612, RS780_9612, RS780) +CHIPSET(0x9613, RS780_9613, RS780) +CHIPSET(0x9614, RS780_9614, RS780) +CHIPSET(0x9615, RS780_9615, RS780) +CHIPSET(0x9616, RS780_9616, RS780) + +CHIPSET(0x9710, RS880_9710, RS880) +CHIPSET(0x9711, RS880_9711, RS880) +CHIPSET(0x9712, RS880_9712, RS880) +CHIPSET(0x9713, RS880_9713, RS880) +CHIPSET(0x9714, RS880_9714, RS880) +CHIPSET(0x9715, RS880_9715, RS880) + +CHIPSET(0x9440, RV770_9440, RV770) +CHIPSET(0x9441, RV770_9441, RV770) +CHIPSET(0x9442, RV770_9442, RV770) +CHIPSET(0x9443, RV770_9443, RV770) +CHIPSET(0x9444, RV770_9444, RV770) +CHIPSET(0x9446, RV770_9446, RV770) +CHIPSET(0x944A, RV770_944A, RV770) +CHIPSET(0x944B, RV770_944B, RV770) +CHIPSET(0x944C, RV770_944C, RV770) +CHIPSET(0x944E, RV770_944E, RV770) +CHIPSET(0x9450, RV770_9450, RV770) +CHIPSET(0x9452, RV770_9452, RV770) +CHIPSET(0x9456, RV770_9456, RV770) +CHIPSET(0x945A, RV770_945A, RV770) +CHIPSET(0x945B, RV770_945B, RV770) +CHIPSET(0x945E, RV770_945E, RV770) +CHIPSET(0x9460, RV790_9460, RV770) +CHIPSET(0x9462, RV790_9462, RV770) +CHIPSET(0x946A, RV770_946A, RV770) +CHIPSET(0x946B, RV770_946B, RV770) +CHIPSET(0x947A, RV770_947A, RV770) +CHIPSET(0x947B, RV770_947B, RV770) + +CHIPSET(0x9480, RV730_9480, RV730) +CHIPSET(0x9487, RV730_9487, RV730) +CHIPSET(0x9488, RV730_9488, RV730) +CHIPSET(0x9489, RV730_9489, RV730) +CHIPSET(0x948A, RV730_948A, RV730) +CHIPSET(0x948F, RV730_948F, RV730) +CHIPSET(0x9490, RV730_9490, RV730) +CHIPSET(0x9491, RV730_9491, RV730) +CHIPSET(0x9495, RV730_9495, RV730) +CHIPSET(0x9498, RV730_9498, RV730) +CHIPSET(0x949C, RV730_949C, RV730) +CHIPSET(0x949E, RV730_949E, RV730) +CHIPSET(0x949F, RV730_949F, RV730) + +CHIPSET(0x9540, RV710_9540, RV710) +CHIPSET(0x9541, RV710_9541, RV710) +CHIPSET(0x9542, RV710_9542, RV710) +CHIPSET(0x954E, RV710_954E, RV710) +CHIPSET(0x954F, RV710_954F, RV710) +CHIPSET(0x9552, RV710_9552, RV710) +CHIPSET(0x9553, RV710_9553, RV710) +CHIPSET(0x9555, RV710_9555, RV710) +CHIPSET(0x9557, RV710_9557, RV710) +CHIPSET(0x955F, RV710_955F, RV710) + +CHIPSET(0x94A0, RV740_94A0, RV740) +CHIPSET(0x94A1, RV740_94A1, RV740) +CHIPSET(0x94A3, RV740_94A3, RV740) +CHIPSET(0x94B1, RV740_94B1, RV740) +CHIPSET(0x94B3, RV740_94B3, RV740) +CHIPSET(0x94B4, RV740_94B4, RV740) +CHIPSET(0x94B5, RV740_94B5, RV740) +CHIPSET(0x94B9, RV740_94B9, RV740) + +CHIPSET(0x68E0, CEDAR_68E0, CEDAR) +CHIPSET(0x68E1, CEDAR_68E1, CEDAR) +CHIPSET(0x68E4, CEDAR_68E4, CEDAR) +CHIPSET(0x68E5, CEDAR_68E5, CEDAR) +CHIPSET(0x68E8, CEDAR_68E8, CEDAR) +CHIPSET(0x68E9, CEDAR_68E9, CEDAR) +CHIPSET(0x68F1, CEDAR_68F1, CEDAR) +CHIPSET(0x68F2, CEDAR_68F2, CEDAR) +CHIPSET(0x68F8, CEDAR_68F8, CEDAR) +CHIPSET(0x68F9, CEDAR_68F9, CEDAR) +CHIPSET(0x68FA, CEDAR_68FA, CEDAR) +CHIPSET(0x68FE, CEDAR_68FE, CEDAR) + +CHIPSET(0x68C0, REDWOOD_68C0, REDWOOD) +CHIPSET(0x68C1, REDWOOD_68C1, REDWOOD) +CHIPSET(0x68C7, REDWOOD_68C7, REDWOOD) +CHIPSET(0x68C8, REDWOOD_68C8, REDWOOD) +CHIPSET(0x68C9, REDWOOD_68C9, REDWOOD) +CHIPSET(0x68D8, REDWOOD_68D8, REDWOOD) +CHIPSET(0x68D9, REDWOOD_68D9, REDWOOD) +CHIPSET(0x68DA, REDWOOD_68DA, REDWOOD) +CHIPSET(0x68DE, REDWOOD_68DE, REDWOOD) + +CHIPSET(0x68A0, JUNIPER_68A0, JUNIPER) +CHIPSET(0x68A1, JUNIPER_68A1, JUNIPER) +CHIPSET(0x68A8, JUNIPER_68A8, JUNIPER) +CHIPSET(0x68A9, JUNIPER_68A9, JUNIPER) +CHIPSET(0x68B0, JUNIPER_68B0, JUNIPER) +CHIPSET(0x68B8, JUNIPER_68B8, JUNIPER) +CHIPSET(0x68B9, JUNIPER_68B9, JUNIPER) +CHIPSET(0x68BA, JUNIPER_68BA, JUNIPER) +CHIPSET(0x68BE, JUNIPER_68BE, JUNIPER) +CHIPSET(0x68BF, JUNIPER_68BF, JUNIPER) + +CHIPSET(0x6880, CYPRESS_6880, CYPRESS) +CHIPSET(0x6888, CYPRESS_6888, CYPRESS) +CHIPSET(0x6889, CYPRESS_6889, CYPRESS) +CHIPSET(0x688A, CYPRESS_688A, CYPRESS) +CHIPSET(0x688C, CYPRESS_688C, CYPRESS) +CHIPSET(0x688D, CYPRESS_688D, CYPRESS) +CHIPSET(0x6898, CYPRESS_6898, CYPRESS) +CHIPSET(0x6899, CYPRESS_6899, CYPRESS) +CHIPSET(0x689B, CYPRESS_689B, CYPRESS) +CHIPSET(0x689E, CYPRESS_689E, CYPRESS) + +CHIPSET(0x689C, HEMLOCK_689C, HEMLOCK) +CHIPSET(0x689D, HEMLOCK_689D, HEMLOCK) + +CHIPSET(0x9802, PALM_9802, PALM) +CHIPSET(0x9803, PALM_9803, PALM) +CHIPSET(0x9804, PALM_9804, PALM) +CHIPSET(0x9805, PALM_9805, PALM) +CHIPSET(0x9806, PALM_9806, PALM) +CHIPSET(0x9807, PALM_9807, PALM) +CHIPSET(0x9808, PALM_9808, PALM) +CHIPSET(0x9809, PALM_9809, PALM) +CHIPSET(0x980A, PALM_980A, PALM) + +CHIPSET(0x9640, SUMO_9640, SUMO) +CHIPSET(0x9641, SUMO_9641, SUMO) +CHIPSET(0x9642, SUMO2_9642, SUMO2) +CHIPSET(0x9643, SUMO2_9643, SUMO2) +CHIPSET(0x9644, SUMO2_9644, SUMO2) +CHIPSET(0x9645, SUMO2_9645, SUMO2) +CHIPSET(0x9647, SUMO_9647, SUMO) +CHIPSET(0x9648, SUMO_9648, SUMO) +CHIPSET(0x9649, SUMO2_9649, SUMO2) +CHIPSET(0x964a, SUMO_964A, SUMO) +CHIPSET(0x964b, SUMO_964B, SUMO) +CHIPSET(0x964c, SUMO_964C, SUMO) +CHIPSET(0x964e, SUMO_964E, SUMO) +CHIPSET(0x964f, SUMO_964F, SUMO) + +CHIPSET(0x6700, CAYMAN_6700, CAYMAN) +CHIPSET(0x6701, CAYMAN_6701, CAYMAN) +CHIPSET(0x6702, CAYMAN_6702, CAYMAN) +CHIPSET(0x6703, CAYMAN_6703, CAYMAN) +CHIPSET(0x6704, CAYMAN_6704, CAYMAN) +CHIPSET(0x6705, CAYMAN_6705, CAYMAN) +CHIPSET(0x6706, CAYMAN_6706, CAYMAN) +CHIPSET(0x6707, CAYMAN_6707, CAYMAN) +CHIPSET(0x6708, CAYMAN_6708, CAYMAN) +CHIPSET(0x6709, CAYMAN_6709, CAYMAN) +CHIPSET(0x6718, CAYMAN_6718, CAYMAN) +CHIPSET(0x6719, CAYMAN_6719, CAYMAN) +CHIPSET(0x671C, CAYMAN_671C, CAYMAN) +CHIPSET(0x671D, CAYMAN_671D, CAYMAN) +CHIPSET(0x671F, CAYMAN_671F, CAYMAN) + +CHIPSET(0x6720, BARTS_6720, BARTS) +CHIPSET(0x6721, BARTS_6721, BARTS) +CHIPSET(0x6722, BARTS_6722, BARTS) +CHIPSET(0x6723, BARTS_6723, BARTS) +CHIPSET(0x6724, BARTS_6724, BARTS) +CHIPSET(0x6725, BARTS_6725, BARTS) +CHIPSET(0x6726, BARTS_6726, BARTS) +CHIPSET(0x6727, BARTS_6727, BARTS) +CHIPSET(0x6728, BARTS_6728, BARTS) +CHIPSET(0x6729, BARTS_6729, BARTS) +CHIPSET(0x6738, BARTS_6738, BARTS) +CHIPSET(0x6739, BARTS_6739, BARTS) +CHIPSET(0x673E, BARTS_673E, BARTS) + +CHIPSET(0x6740, TURKS_6740, TURKS) +CHIPSET(0x6741, TURKS_6741, TURKS) +CHIPSET(0x6742, TURKS_6742, TURKS) +CHIPSET(0x6743, TURKS_6743, TURKS) +CHIPSET(0x6744, TURKS_6744, TURKS) +CHIPSET(0x6745, TURKS_6745, TURKS) +CHIPSET(0x6746, TURKS_6746, TURKS) +CHIPSET(0x6747, TURKS_6747, TURKS) +CHIPSET(0x6748, TURKS_6748, TURKS) +CHIPSET(0x6749, TURKS_6749, TURKS) +CHIPSET(0x674A, TURKS_674A, TURKS) +CHIPSET(0x6750, TURKS_6750, TURKS) +CHIPSET(0x6751, TURKS_6751, TURKS) +CHIPSET(0x6758, TURKS_6758, TURKS) +CHIPSET(0x6759, TURKS_6759, TURKS) +CHIPSET(0x675B, TURKS_675B, TURKS) +CHIPSET(0x675D, TURKS_675D, TURKS) +CHIPSET(0x675F, TURKS_675F, TURKS) +CHIPSET(0x6840, TURKS_6840, TURKS) +CHIPSET(0x6841, TURKS_6841, TURKS) +CHIPSET(0x6842, TURKS_6842, TURKS) +CHIPSET(0x6843, TURKS_6843, TURKS) +CHIPSET(0x6849, TURKS_6849, TURKS) +CHIPSET(0x6850, TURKS_6850, TURKS) +CHIPSET(0x6858, TURKS_6858, TURKS) +CHIPSET(0x6859, TURKS_6859, TURKS) + +CHIPSET(0x6760, CAICOS_6760, CAICOS) +CHIPSET(0x6761, CAICOS_6761, CAICOS) +CHIPSET(0x6762, CAICOS_6762, CAICOS) +CHIPSET(0x6763, CAICOS_6763, CAICOS) +CHIPSET(0x6764, CAICOS_6764, CAICOS) +CHIPSET(0x6765, CAICOS_6765, CAICOS) +CHIPSET(0x6766, CAICOS_6766, CAICOS) +CHIPSET(0x6767, CAICOS_6767, CAICOS) +CHIPSET(0x6768, CAICOS_6768, CAICOS) +CHIPSET(0x6770, CAICOS_6770, CAICOS) +CHIPSET(0x6771, CAICOS_6771, CAICOS) +CHIPSET(0x6772, CAICOS_6772, CAICOS) +CHIPSET(0x6778, CAICOS_6778, CAICOS) +CHIPSET(0x6779, CAICOS_6779, CAICOS) +CHIPSET(0x677B, CAICOS_677B, CAICOS) + +CHIPSET(0x9900, ARUBA_9900, ARUBA) +CHIPSET(0x9901, ARUBA_9901, ARUBA) +CHIPSET(0x9903, ARUBA_9903, ARUBA) +CHIPSET(0x9904, ARUBA_9904, ARUBA) +CHIPSET(0x9905, ARUBA_9905, ARUBA) +CHIPSET(0x9906, ARUBA_9906, ARUBA) +CHIPSET(0x9907, ARUBA_9907, ARUBA) +CHIPSET(0x9908, ARUBA_9908, ARUBA) +CHIPSET(0x9909, ARUBA_9909, ARUBA) +CHIPSET(0x990A, ARUBA_990A, ARUBA) +CHIPSET(0x990B, ARUBA_990B, ARUBA) +CHIPSET(0x990C, ARUBA_990C, ARUBA) +CHIPSET(0x990D, ARUBA_990D, ARUBA) +CHIPSET(0x990E, ARUBA_990E, ARUBA) +CHIPSET(0x990F, ARUBA_990F, ARUBA) +CHIPSET(0x9910, ARUBA_9910, ARUBA) +CHIPSET(0x9913, ARUBA_9913, ARUBA) +CHIPSET(0x9917, ARUBA_9917, ARUBA) +CHIPSET(0x9918, ARUBA_9918, ARUBA) +CHIPSET(0x9919, ARUBA_9919, ARUBA) +CHIPSET(0x9990, ARUBA_9990, ARUBA) +CHIPSET(0x9991, ARUBA_9991, ARUBA) +CHIPSET(0x9992, ARUBA_9992, ARUBA) +CHIPSET(0x9993, ARUBA_9993, ARUBA) +CHIPSET(0x9994, ARUBA_9994, ARUBA) +CHIPSET(0x9995, ARUBA_9995, ARUBA) +CHIPSET(0x9996, ARUBA_9996, ARUBA) +CHIPSET(0x9997, ARUBA_9997, ARUBA) +CHIPSET(0x9998, ARUBA_9998, ARUBA) +CHIPSET(0x9999, ARUBA_9999, ARUBA) +CHIPSET(0x999A, ARUBA_999A, ARUBA) +CHIPSET(0x999B, ARUBA_999B, ARUBA) +CHIPSET(0x999C, ARUBA_999C, ARUBA) +CHIPSET(0x999D, ARUBA_999D, ARUBA) +CHIPSET(0x99A0, ARUBA_99A0, ARUBA) +CHIPSET(0x99A2, ARUBA_99A2, ARUBA) +CHIPSET(0x99A4, ARUBA_99A4, ARUBA) + +CHIPSET(0x6780, TAHITI_6780, TAHITI) +CHIPSET(0x6784, TAHITI_6784, TAHITI) +CHIPSET(0x6788, TAHITI_6788, TAHITI) +CHIPSET(0x678A, TAHITI_678A, TAHITI) +CHIPSET(0x6790, TAHITI_6790, TAHITI) +CHIPSET(0x6791, TAHITI_6791, TAHITI) +CHIPSET(0x6792, TAHITI_6792, TAHITI) +CHIPSET(0x6798, TAHITI_6798, TAHITI) +CHIPSET(0x6799, TAHITI_6799, TAHITI) +CHIPSET(0x679A, TAHITI_679A, TAHITI) +CHIPSET(0x679B, TAHITI_679B, TAHITI) +CHIPSET(0x679E, TAHITI_679E, TAHITI) +CHIPSET(0x679F, TAHITI_679F, TAHITI) + +CHIPSET(0x6800, PITCAIRN_6800, PITCAIRN) +CHIPSET(0x6801, PITCAIRN_6801, PITCAIRN) +CHIPSET(0x6802, PITCAIRN_6802, PITCAIRN) +CHIPSET(0x6806, PITCAIRN_6806, PITCAIRN) +CHIPSET(0x6808, PITCAIRN_6808, PITCAIRN) +CHIPSET(0x6809, PITCAIRN_6809, PITCAIRN) +CHIPSET(0x6810, PITCAIRN_6810, PITCAIRN) +CHIPSET(0x6811, PITCAIRN_6811, PITCAIRN) +CHIPSET(0x6816, PITCAIRN_6816, PITCAIRN) +CHIPSET(0x6817, PITCAIRN_6817, PITCAIRN) +CHIPSET(0x6818, PITCAIRN_6818, PITCAIRN) +CHIPSET(0x6819, PITCAIRN_6819, PITCAIRN) +CHIPSET(0x684C, PITCAIRN_684C, PITCAIRN) + +CHIPSET(0x6820, VERDE_6820, VERDE) +CHIPSET(0x6821, VERDE_6821, VERDE) +CHIPSET(0x6822, VERDE_6822, VERDE) +CHIPSET(0x6823, VERDE_6823, VERDE) +CHIPSET(0x6824, VERDE_6824, VERDE) +CHIPSET(0x6825, VERDE_6825, VERDE) +CHIPSET(0x6826, VERDE_6826, VERDE) +CHIPSET(0x6827, VERDE_6827, VERDE) +CHIPSET(0x6828, VERDE_6828, VERDE) +CHIPSET(0x6829, VERDE_6829, VERDE) +CHIPSET(0x682A, VERDE_682A, VERDE) +CHIPSET(0x682B, VERDE_682B, VERDE) +CHIPSET(0x682C, VERDE_682C, VERDE) +CHIPSET(0x682D, VERDE_682D, VERDE) +CHIPSET(0x682F, VERDE_682F, VERDE) +CHIPSET(0x6830, VERDE_6830, VERDE) +CHIPSET(0x6831, VERDE_6831, VERDE) +CHIPSET(0x6835, VERDE_6835, VERDE) +CHIPSET(0x6837, VERDE_6837, VERDE) +CHIPSET(0x6838, VERDE_6838, VERDE) +CHIPSET(0x6839, VERDE_6839, VERDE) +CHIPSET(0x683B, VERDE_683B, VERDE) +CHIPSET(0x683D, VERDE_683D, VERDE) +CHIPSET(0x683F, VERDE_683F, VERDE) + +CHIPSET(0x6600, OLAND_6600, OLAND) +CHIPSET(0x6601, OLAND_6601, OLAND) +CHIPSET(0x6602, OLAND_6602, OLAND) +CHIPSET(0x6603, OLAND_6603, OLAND) +CHIPSET(0x6604, OLAND_6604, OLAND) +CHIPSET(0x6605, OLAND_6605, OLAND) +CHIPSET(0x6606, OLAND_6606, OLAND) +CHIPSET(0x6607, OLAND_6607, OLAND) +CHIPSET(0x6608, OLAND_6608, OLAND) +CHIPSET(0x6610, OLAND_6610, OLAND) +CHIPSET(0x6611, OLAND_6611, OLAND) +CHIPSET(0x6613, OLAND_6613, OLAND) +CHIPSET(0x6617, OLAND_6617, OLAND) +CHIPSET(0x6620, OLAND_6620, OLAND) +CHIPSET(0x6621, OLAND_6621, OLAND) +CHIPSET(0x6623, OLAND_6623, OLAND) +CHIPSET(0x6631, OLAND_6631, OLAND) + +CHIPSET(0x6660, HAINAN_6660, HAINAN) +CHIPSET(0x6663, HAINAN_6663, HAINAN) +CHIPSET(0x6664, HAINAN_6664, HAINAN) +CHIPSET(0x6665, HAINAN_6665, HAINAN) +CHIPSET(0x6667, HAINAN_6667, HAINAN) +CHIPSET(0x666F, HAINAN_666F, HAINAN) + +CHIPSET(0x6640, BONAIRE_6640, BONAIRE) +CHIPSET(0x6641, BONAIRE_6641, BONAIRE) +CHIPSET(0x6646, BONAIRE_6646, BONAIRE) +CHIPSET(0x6647, BONAIRE_6647, BONAIRE) +CHIPSET(0x6649, BONAIRE_6649, BONAIRE) +CHIPSET(0x6650, BONAIRE_6650, BONAIRE) +CHIPSET(0x6651, BONAIRE_6651, BONAIRE) +CHIPSET(0x6658, BONAIRE_6658, BONAIRE) +CHIPSET(0x665C, BONAIRE_665C, BONAIRE) +CHIPSET(0x665D, BONAIRE_665D, BONAIRE) +CHIPSET(0x665F, BONAIRE_665F, BONAIRE) + +CHIPSET(0x9830, KABINI_9830, KABINI) +CHIPSET(0x9831, KABINI_9831, KABINI) +CHIPSET(0x9832, KABINI_9832, KABINI) +CHIPSET(0x9833, KABINI_9833, KABINI) +CHIPSET(0x9834, KABINI_9834, KABINI) +CHIPSET(0x9835, KABINI_9835, KABINI) +CHIPSET(0x9836, KABINI_9836, KABINI) +CHIPSET(0x9837, KABINI_9837, KABINI) +CHIPSET(0x9838, KABINI_9838, KABINI) +CHIPSET(0x9839, KABINI_9839, KABINI) +CHIPSET(0x983A, KABINI_983A, KABINI) +CHIPSET(0x983B, KABINI_983B, KABINI) +CHIPSET(0x983C, KABINI_983C, KABINI) +CHIPSET(0x983D, KABINI_983D, KABINI) +CHIPSET(0x983E, KABINI_983E, KABINI) +CHIPSET(0x983F, KABINI_983F, KABINI) + +CHIPSET(0x9850, MULLINS_9850, MULLINS) +CHIPSET(0x9851, MULLINS_9851, MULLINS) +CHIPSET(0x9852, MULLINS_9852, MULLINS) +CHIPSET(0x9853, MULLINS_9853, MULLINS) +CHIPSET(0x9854, MULLINS_9854, MULLINS) +CHIPSET(0x9855, MULLINS_9855, MULLINS) +CHIPSET(0x9856, MULLINS_9856, MULLINS) +CHIPSET(0x9857, MULLINS_9857, MULLINS) +CHIPSET(0x9858, MULLINS_9858, MULLINS) +CHIPSET(0x9859, MULLINS_9859, MULLINS) +CHIPSET(0x985A, MULLINS_985A, MULLINS) +CHIPSET(0x985B, MULLINS_985B, MULLINS) +CHIPSET(0x985C, MULLINS_985C, MULLINS) +CHIPSET(0x985D, MULLINS_985D, MULLINS) +CHIPSET(0x985E, MULLINS_985E, MULLINS) +CHIPSET(0x985F, MULLINS_985F, MULLINS) + +CHIPSET(0x1304, KAVERI_1304, KAVERI) +CHIPSET(0x1305, KAVERI_1305, KAVERI) +CHIPSET(0x1306, KAVERI_1306, KAVERI) +CHIPSET(0x1307, KAVERI_1307, KAVERI) +CHIPSET(0x1309, KAVERI_1309, KAVERI) +CHIPSET(0x130A, KAVERI_130A, KAVERI) +CHIPSET(0x130B, KAVERI_130B, KAVERI) +CHIPSET(0x130C, KAVERI_130C, KAVERI) +CHIPSET(0x130D, KAVERI_130D, KAVERI) +CHIPSET(0x130E, KAVERI_130E, KAVERI) +CHIPSET(0x130F, KAVERI_130F, KAVERI) +CHIPSET(0x1310, KAVERI_1310, KAVERI) +CHIPSET(0x1311, KAVERI_1311, KAVERI) +CHIPSET(0x1312, KAVERI_1312, KAVERI) +CHIPSET(0x1313, KAVERI_1313, KAVERI) +CHIPSET(0x1315, KAVERI_1315, KAVERI) +CHIPSET(0x1316, KAVERI_1316, KAVERI) +CHIPSET(0x1317, KAVERI_1317, KAVERI) +CHIPSET(0x1318, KAVERI_1318, KAVERI) +CHIPSET(0x131B, KAVERI_131B, KAVERI) +CHIPSET(0x131C, KAVERI_131C, KAVERI) +CHIPSET(0x131D, KAVERI_131D, KAVERI) + +CHIPSET(0x67A0, HAWAII_67A0, HAWAII) +CHIPSET(0x67A1, HAWAII_67A1, HAWAII) +CHIPSET(0x67A2, HAWAII_67A2, HAWAII) +CHIPSET(0x67A8, HAWAII_67A8, HAWAII) +CHIPSET(0x67A9, HAWAII_67A9, HAWAII) +CHIPSET(0x67AA, HAWAII_67AA, HAWAII) +CHIPSET(0x67B0, HAWAII_67B0, HAWAII) +CHIPSET(0x67B1, HAWAII_67B1, HAWAII) +CHIPSET(0x67B8, HAWAII_67B8, HAWAII) +CHIPSET(0x67B9, HAWAII_67B9, HAWAII) +CHIPSET(0x67BA, HAWAII_67BA, HAWAII) +CHIPSET(0x67BE, HAWAII_67BE, HAWAII) diff --git a/radeon/radeon-symbols.txt b/radeon/radeon-symbols.txt new file mode 100644 index 0000000..5a532d8 --- /dev/null +++ b/radeon/radeon-symbols.txt @@ -0,0 +1,44 @@ +radeon_bo_debug +radeon_bo_get_handle +radeon_bo_get_src_domain +radeon_bo_get_tiling +radeon_bo_is_busy +radeon_bo_is_referenced_by_cs +radeon_bo_is_static +radeon_bo_manager_gem_ctor +radeon_bo_manager_gem_dtor +radeon_bo_map +radeon_bo_open +radeon_bo_ref +radeon_bo_set_tiling +radeon_bo_unmap +radeon_bo_unref +radeon_bo_wait +radeon_cs_begin +radeon_cs_create +radeon_cs_destroy +radeon_cs_emit +radeon_cs_end +radeon_cs_erase +radeon_cs_get_id +radeon_cs_manager_gem_ctor +radeon_cs_manager_gem_dtor +radeon_cs_need_flush +radeon_cs_print +radeon_cs_set_limit +radeon_cs_space_add_persistent_bo +radeon_cs_space_check +radeon_cs_space_check_with_bo +radeon_cs_space_reset_bos +radeon_cs_space_set_flush +radeon_cs_write_reloc +radeon_gem_bo_open_prime +radeon_gem_get_kernel_name +radeon_gem_get_reloc_in_cs +radeon_gem_name_bo +radeon_gem_prime_share_bo +radeon_gem_set_domain +radeon_surface_best +radeon_surface_init +radeon_surface_manager_free +radeon_surface_manager_new diff --git a/radeon/radeon_bo.c b/radeon/radeon_bo.c new file mode 100644 index 0000000..9192953 --- /dev/null +++ b/radeon/radeon_bo.c @@ -0,0 +1,142 @@ +/* + * Copyright © 2008 Dave Airlie + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Dave Airlie + * Jérôme Glisse <glisse@freedesktop.org> + */ +#include <libdrm_macros.h> +#include <radeon_bo.h> +#include <radeon_bo_int.h> + +drm_public void radeon_bo_debug(struct radeon_bo *bo, const char *op) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + + fprintf(stderr, "%s %p 0x%08X 0x%08X 0x%08X\n", + op, bo, bo->handle, boi->size, boi->cref); +} + +drm_public struct radeon_bo * +radeon_bo_open(struct radeon_bo_manager *bom, uint32_t handle, uint32_t size, + uint32_t alignment, uint32_t domains, uint32_t flags) +{ + struct radeon_bo *bo; + bo = bom->funcs->bo_open(bom, handle, size, alignment, domains, flags); + return bo; +} + +drm_public void radeon_bo_ref(struct radeon_bo *bo) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + boi->cref++; + boi->bom->funcs->bo_ref(boi); +} + +drm_public struct radeon_bo *radeon_bo_unref(struct radeon_bo *bo) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + if (bo == NULL) + return NULL; + + boi->cref--; + return boi->bom->funcs->bo_unref(boi); +} + +drm_public int radeon_bo_map(struct radeon_bo *bo, int write) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + return boi->bom->funcs->bo_map(boi, write); +} + +drm_public int radeon_bo_unmap(struct radeon_bo *bo) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + return boi->bom->funcs->bo_unmap(boi); +} + +drm_public int radeon_bo_wait(struct radeon_bo *bo) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + if (!boi->bom->funcs->bo_wait) + return 0; + return boi->bom->funcs->bo_wait(boi); +} + +drm_public int radeon_bo_is_busy(struct radeon_bo *bo, uint32_t *domain) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + return boi->bom->funcs->bo_is_busy(boi, domain); +} + +drm_public int +radeon_bo_set_tiling(struct radeon_bo *bo, + uint32_t tiling_flags, uint32_t pitch) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + return boi->bom->funcs->bo_set_tiling(boi, tiling_flags, pitch); +} + +drm_public int +radeon_bo_get_tiling(struct radeon_bo *bo, + uint32_t *tiling_flags, uint32_t *pitch) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + return boi->bom->funcs->bo_get_tiling(boi, tiling_flags, pitch); +} + +drm_public int radeon_bo_is_static(struct radeon_bo *bo) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + if (boi->bom->funcs->bo_is_static) + return boi->bom->funcs->bo_is_static(boi); + return 0; +} + +drm_public int +radeon_bo_is_referenced_by_cs(struct radeon_bo *bo, struct radeon_cs *cs) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + return boi->cref > 1; +} + +drm_public uint32_t radeon_bo_get_handle(struct radeon_bo *bo) +{ + return bo->handle; +} + +drm_public uint32_t radeon_bo_get_src_domain(struct radeon_bo *bo) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + uint32_t src_domain; + + src_domain = boi->space_accounted & 0xffff; + if (!src_domain) + src_domain = boi->space_accounted >> 16; + + return src_domain; +} diff --git a/radeon/radeon_bo.h b/radeon/radeon_bo.h new file mode 100644 index 0000000..6e20c6c --- /dev/null +++ b/radeon/radeon_bo.h @@ -0,0 +1,73 @@ +/* + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_BO_H +#define RADEON_BO_H + +#include <stdio.h> +#include <stdint.h> + +/* bo object */ +#define RADEON_BO_FLAGS_MACRO_TILE 1 +#define RADEON_BO_FLAGS_MICRO_TILE 2 +#define RADEON_BO_FLAGS_MICRO_TILE_SQUARE 0x20 + +struct radeon_bo_manager; +struct radeon_cs; + +struct radeon_bo { + void *ptr; + uint32_t flags; + uint32_t handle; + uint32_t size; +}; + + +void radeon_bo_debug(struct radeon_bo *bo, const char *op); + +struct radeon_bo *radeon_bo_open(struct radeon_bo_manager *bom, + uint32_t handle, + uint32_t size, + uint32_t alignment, + uint32_t domains, + uint32_t flags); + +void radeon_bo_ref(struct radeon_bo *bo); +struct radeon_bo *radeon_bo_unref(struct radeon_bo *bo); +int radeon_bo_map(struct radeon_bo *bo, int write); +int radeon_bo_unmap(struct radeon_bo *bo); +int radeon_bo_wait(struct radeon_bo *bo); +int radeon_bo_is_busy(struct radeon_bo *bo, uint32_t *domain); +int radeon_bo_set_tiling(struct radeon_bo *bo, uint32_t tiling_flags, uint32_t pitch); +int radeon_bo_get_tiling(struct radeon_bo *bo, uint32_t *tiling_flags, uint32_t *pitch); +int radeon_bo_is_static(struct radeon_bo *bo); +int radeon_bo_is_referenced_by_cs(struct radeon_bo *bo, struct radeon_cs *cs); +uint32_t radeon_bo_get_handle(struct radeon_bo *bo); +uint32_t radeon_bo_get_src_domain(struct radeon_bo *bo); +#endif diff --git a/radeon/radeon_bo_gem.c b/radeon/radeon_bo_gem.c new file mode 100644 index 0000000..bbe72ce --- /dev/null +++ b/radeon/radeon_bo_gem.c @@ -0,0 +1,399 @@ +/* + * Copyright © 2008 Dave Airlie + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Dave Airlie + * Jérôme Glisse <glisse@freedesktop.org> + */ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "libdrm_macros.h" +#include "xf86drm.h" +#include "xf86atomic.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_bo.h" +#include "radeon_bo_int.h" +#include "radeon_bo_gem.h" +#include <fcntl.h> +struct radeon_bo_gem { + struct radeon_bo_int base; + uint32_t name; + int map_count; + atomic_t reloc_in_cs; + void *priv_ptr; +}; + +struct bo_manager_gem { + struct radeon_bo_manager base; +}; + +static int bo_wait(struct radeon_bo_int *boi); + +static struct radeon_bo *bo_open(struct radeon_bo_manager *bom, + uint32_t handle, + uint32_t size, + uint32_t alignment, + uint32_t domains, + uint32_t flags) +{ + struct radeon_bo_gem *bo; + int r; + + bo = (struct radeon_bo_gem*)calloc(1, sizeof(struct radeon_bo_gem)); + if (bo == NULL) { + return NULL; + } + + bo->base.bom = bom; + bo->base.handle = 0; + bo->base.size = size; + bo->base.alignment = alignment; + bo->base.domains = domains; + bo->base.flags = flags; + bo->base.ptr = NULL; + atomic_set(&bo->reloc_in_cs, 0); + bo->map_count = 0; + if (handle) { + struct drm_gem_open open_arg; + + memset(&open_arg, 0, sizeof(open_arg)); + open_arg.name = handle; + r = drmIoctl(bom->fd, DRM_IOCTL_GEM_OPEN, &open_arg); + if (r != 0) { + free(bo); + return NULL; + } + bo->base.handle = open_arg.handle; + bo->base.size = open_arg.size; + bo->name = handle; + } else { + struct drm_radeon_gem_create args; + + args.size = size; + args.alignment = alignment; + args.initial_domain = bo->base.domains; + args.flags = flags; + args.handle = 0; + r = drmCommandWriteRead(bom->fd, DRM_RADEON_GEM_CREATE, + &args, sizeof(args)); + bo->base.handle = args.handle; + if (r) { + fprintf(stderr, "Failed to allocate :\n"); + fprintf(stderr, " size : %d bytes\n", size); + fprintf(stderr, " alignment : %d bytes\n", alignment); + fprintf(stderr, " domains : %d\n", bo->base.domains); + free(bo); + return NULL; + } + } + radeon_bo_ref((struct radeon_bo*)bo); + return (struct radeon_bo*)bo; +} + +static void bo_ref(struct radeon_bo_int *boi) +{ +} + +static struct radeon_bo *bo_unref(struct radeon_bo_int *boi) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)boi; + + if (boi->cref) { + return (struct radeon_bo *)boi; + } + if (bo_gem->priv_ptr) { + drm_munmap(bo_gem->priv_ptr, boi->size); + } + + /* close object */ + drmCloseBufferHandle(boi->bom->fd, boi->handle); + memset(bo_gem, 0, sizeof(struct radeon_bo_gem)); + free(bo_gem); + return NULL; +} + +static int bo_map(struct radeon_bo_int *boi, int write) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)boi; + struct drm_radeon_gem_mmap args; + int r; + void *ptr; + + if (bo_gem->map_count++ != 0) { + return 0; + } + if (bo_gem->priv_ptr) { + goto wait; + } + + boi->ptr = NULL; + + /* Zero out args to make valgrind happy */ + memset(&args, 0, sizeof(args)); + args.handle = boi->handle; + args.offset = 0; + args.size = (uint64_t)boi->size; + r = drmCommandWriteRead(boi->bom->fd, + DRM_RADEON_GEM_MMAP, + &args, + sizeof(args)); + if (r) { + fprintf(stderr, "error mapping %p 0x%08X (error = %d)\n", + boi, boi->handle, r); + return r; + } + ptr = drm_mmap(0, args.size, PROT_READ|PROT_WRITE, MAP_SHARED, boi->bom->fd, args.addr_ptr); + if (ptr == MAP_FAILED) + return -errno; + bo_gem->priv_ptr = ptr; +wait: + boi->ptr = bo_gem->priv_ptr; + r = bo_wait(boi); + if (r) + return r; + return 0; +} + +static int bo_unmap(struct radeon_bo_int *boi) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)boi; + + if (--bo_gem->map_count > 0) { + return 0; + } + //drm_munmap(bo->ptr, bo->size); + boi->ptr = NULL; + return 0; +} + +static int bo_wait(struct radeon_bo_int *boi) +{ + struct drm_radeon_gem_wait_idle args; + int ret; + + /* Zero out args to make valgrind happy */ + memset(&args, 0, sizeof(args)); + args.handle = boi->handle; + do { + ret = drmCommandWrite(boi->bom->fd, DRM_RADEON_GEM_WAIT_IDLE, + &args, sizeof(args)); + } while (ret == -EBUSY); + return ret; +} + +static int bo_is_busy(struct radeon_bo_int *boi, uint32_t *domain) +{ + struct drm_radeon_gem_busy args; + int ret; + + args.handle = boi->handle; + args.domain = 0; + + ret = drmCommandWriteRead(boi->bom->fd, DRM_RADEON_GEM_BUSY, + &args, sizeof(args)); + + *domain = args.domain; + return ret; +} + +static int bo_set_tiling(struct radeon_bo_int *boi, uint32_t tiling_flags, + uint32_t pitch) +{ + struct drm_radeon_gem_set_tiling args; + int r; + + args.handle = boi->handle; + args.tiling_flags = tiling_flags; + args.pitch = pitch; + + r = drmCommandWriteRead(boi->bom->fd, + DRM_RADEON_GEM_SET_TILING, + &args, + sizeof(args)); + return r; +} + +static int bo_get_tiling(struct radeon_bo_int *boi, uint32_t *tiling_flags, + uint32_t *pitch) +{ + struct drm_radeon_gem_set_tiling args = {}; + int r; + + args.handle = boi->handle; + + r = drmCommandWriteRead(boi->bom->fd, + DRM_RADEON_GEM_GET_TILING, + &args, + sizeof(args)); + + if (r) + return r; + + *tiling_flags = args.tiling_flags; + *pitch = args.pitch; + return r; +} + +static const struct radeon_bo_funcs bo_gem_funcs = { + .bo_open = bo_open, + .bo_ref = bo_ref, + .bo_unref = bo_unref, + .bo_map = bo_map, + .bo_unmap = bo_unmap, + .bo_wait = bo_wait, + .bo_is_static = NULL, + .bo_set_tiling = bo_set_tiling, + .bo_get_tiling = bo_get_tiling, + .bo_is_busy = bo_is_busy, + .bo_is_referenced_by_cs = NULL, +}; + +drm_public struct radeon_bo_manager *radeon_bo_manager_gem_ctor(int fd) +{ + struct bo_manager_gem *bomg; + + bomg = (struct bo_manager_gem*)calloc(1, sizeof(struct bo_manager_gem)); + if (bomg == NULL) { + return NULL; + } + bomg->base.funcs = &bo_gem_funcs; + bomg->base.fd = fd; + return (struct radeon_bo_manager*)bomg; +} + +drm_public void radeon_bo_manager_gem_dtor(struct radeon_bo_manager *bom) +{ + struct bo_manager_gem *bomg = (struct bo_manager_gem*)bom; + + if (bom == NULL) { + return; + } + free(bomg); +} + +drm_public uint32_t +radeon_gem_name_bo(struct radeon_bo *bo) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + return bo_gem->name; +} + +drm_public void * +radeon_gem_get_reloc_in_cs(struct radeon_bo *bo) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + return &bo_gem->reloc_in_cs; +} + +drm_public int +radeon_gem_get_kernel_name(struct radeon_bo *bo, uint32_t *name) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + struct drm_gem_flink flink; + int r; + + if (bo_gem->name) { + *name = bo_gem->name; + return 0; + } + flink.handle = bo->handle; + r = drmIoctl(boi->bom->fd, DRM_IOCTL_GEM_FLINK, &flink); + if (r) { + return r; + } + bo_gem->name = flink.name; + *name = flink.name; + return 0; +} + +drm_public int +radeon_gem_set_domain(struct radeon_bo *bo, uint32_t read_domains, uint32_t write_domain) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + struct drm_radeon_gem_set_domain args; + int r; + + args.handle = bo->handle; + args.read_domains = read_domains; + args.write_domain = write_domain; + + r = drmCommandWriteRead(boi->bom->fd, + DRM_RADEON_GEM_SET_DOMAIN, + &args, + sizeof(args)); + return r; +} + +drm_public int radeon_gem_prime_share_bo(struct radeon_bo *bo, int *handle) +{ + struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; + int ret; + + ret = drmPrimeHandleToFD(bo_gem->base.bom->fd, bo->handle, DRM_CLOEXEC, handle); + return ret; +} + +drm_public struct radeon_bo * +radeon_gem_bo_open_prime(struct radeon_bo_manager *bom, int fd_handle, uint32_t size) +{ + struct radeon_bo_gem *bo; + int r; + uint32_t handle; + + bo = (struct radeon_bo_gem*)calloc(1, sizeof(struct radeon_bo_gem)); + if (bo == NULL) { + return NULL; + } + + bo->base.bom = bom; + bo->base.handle = 0; + bo->base.size = size; + bo->base.alignment = 0; + bo->base.domains = RADEON_GEM_DOMAIN_GTT; + bo->base.flags = 0; + bo->base.ptr = NULL; + atomic_set(&bo->reloc_in_cs, 0); + bo->map_count = 0; + + r = drmPrimeFDToHandle(bom->fd, fd_handle, &handle); + if (r != 0) { + free(bo); + return NULL; + } + + bo->base.handle = handle; + bo->name = handle; + + radeon_bo_ref((struct radeon_bo *)bo); + return (struct radeon_bo *)bo; + +} diff --git a/radeon/radeon_bo_gem.h b/radeon/radeon_bo_gem.h new file mode 100644 index 0000000..08965f3 --- /dev/null +++ b/radeon/radeon_bo_gem.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2008 Dave Airlie + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Dave Airlie + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_BO_GEM_H +#define RADEON_BO_GEM_H + +#include "radeon_bo.h" + +struct radeon_bo_manager *radeon_bo_manager_gem_ctor(int fd); +void radeon_bo_manager_gem_dtor(struct radeon_bo_manager *bom); + +uint32_t radeon_gem_name_bo(struct radeon_bo *bo); +void *radeon_gem_get_reloc_in_cs(struct radeon_bo *bo); +int radeon_gem_set_domain(struct radeon_bo *bo, uint32_t read_domains, uint32_t write_domain); +int radeon_gem_get_kernel_name(struct radeon_bo *bo, uint32_t *name); +int radeon_gem_prime_share_bo(struct radeon_bo *bo, int *handle); +struct radeon_bo *radeon_gem_bo_open_prime(struct radeon_bo_manager *bom, + int fd_handle, + uint32_t size); +#endif diff --git a/radeon/radeon_bo_int.h b/radeon/radeon_bo_int.h new file mode 100644 index 0000000..de981b0 --- /dev/null +++ b/radeon/radeon_bo_int.h @@ -0,0 +1,45 @@ +#ifndef RADEON_BO_INT +#define RADEON_BO_INT + +struct radeon_bo_manager { + const struct radeon_bo_funcs *funcs; + int fd; +}; + +struct radeon_bo_int { + void *ptr; + uint32_t flags; + uint32_t handle; + uint32_t size; + /* private members */ + uint32_t alignment; + uint32_t domains; + unsigned cref; + struct radeon_bo_manager *bom; + uint32_t space_accounted; + uint32_t referenced_in_cs; +}; + +/* bo functions */ +struct radeon_bo_funcs { + struct radeon_bo *(*bo_open)(struct radeon_bo_manager *bom, + uint32_t handle, + uint32_t size, + uint32_t alignment, + uint32_t domains, + uint32_t flags); + void (*bo_ref)(struct radeon_bo_int *bo); + struct radeon_bo *(*bo_unref)(struct radeon_bo_int *bo); + int (*bo_map)(struct radeon_bo_int *bo, int write); + int (*bo_unmap)(struct radeon_bo_int *bo); + int (*bo_wait)(struct radeon_bo_int *bo); + int (*bo_is_static)(struct radeon_bo_int *bo); + int (*bo_set_tiling)(struct radeon_bo_int *bo, uint32_t tiling_flags, + uint32_t pitch); + int (*bo_get_tiling)(struct radeon_bo_int *bo, uint32_t *tiling_flags, + uint32_t *pitch); + int (*bo_is_busy)(struct radeon_bo_int *bo, uint32_t *domain); + int (*bo_is_referenced_by_cs)(struct radeon_bo_int *bo, struct radeon_cs *cs); +}; + +#endif diff --git a/radeon/radeon_cs.c b/radeon/radeon_cs.c new file mode 100644 index 0000000..1132d06 --- /dev/null +++ b/radeon/radeon_cs.c @@ -0,0 +1,95 @@ +#include "libdrm_macros.h" +#include <stdio.h> +#include "radeon_cs.h" +#include "radeon_cs_int.h" + +drm_public struct radeon_cs * +radeon_cs_create(struct radeon_cs_manager *csm, uint32_t ndw) +{ + struct radeon_cs_int *csi = csm->funcs->cs_create(csm, ndw); + return (struct radeon_cs *)csi; +} + +drm_public int +radeon_cs_write_reloc(struct radeon_cs *cs, struct radeon_bo *bo, + uint32_t read_domain, uint32_t write_domain, + uint32_t flags) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + + return csi->csm->funcs->cs_write_reloc(csi, + bo, + read_domain, + write_domain, + flags); +} + +drm_public int +radeon_cs_begin(struct radeon_cs *cs, uint32_t ndw, + const char *file, const char *func, int line) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->csm->funcs->cs_begin(csi, ndw, file, func, line); +} + +drm_public int +radeon_cs_end(struct radeon_cs *cs, + const char *file, const char *func, int line) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->csm->funcs->cs_end(csi, file, func, line); +} + +drm_public int radeon_cs_emit(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->csm->funcs->cs_emit(csi); +} + +drm_public int radeon_cs_destroy(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->csm->funcs->cs_destroy(csi); +} + +drm_public int radeon_cs_erase(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->csm->funcs->cs_erase(csi); +} + +drm_public int radeon_cs_need_flush(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->csm->funcs->cs_need_flush(csi); +} + +drm_public void radeon_cs_print(struct radeon_cs *cs, FILE *file) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + csi->csm->funcs->cs_print(csi, file); +} + +drm_public void +radeon_cs_set_limit(struct radeon_cs *cs, uint32_t domain, uint32_t limit) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + if (domain == RADEON_GEM_DOMAIN_VRAM) + csi->csm->vram_limit = limit; + else + csi->csm->gart_limit = limit; +} + +drm_public void radeon_cs_space_set_flush(struct radeon_cs *cs, + void (*fn)(void *), void *data) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + csi->space_flush_fn = fn; + csi->space_flush_data = data; +} + +drm_public uint32_t radeon_cs_get_id(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return csi->id; +} diff --git a/radeon/radeon_cs.h b/radeon/radeon_cs.h new file mode 100644 index 0000000..f68a624 --- /dev/null +++ b/radeon/radeon_cs.h @@ -0,0 +1,141 @@ +/* + * Copyright © 2008 Nicolai Haehnle + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Aapo Tahkola <aet@rasterburn.org> + * Nicolai Haehnle <prefect_@gmx.net> + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_CS_H +#define RADEON_CS_H + +#include <stdint.h> +#include <string.h> +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_bo.h" + +struct radeon_cs_reloc { + struct radeon_bo *bo; + uint32_t read_domain; + uint32_t write_domain; + uint32_t flags; +}; + + +#define RADEON_CS_SPACE_OK 0 +#define RADEON_CS_SPACE_OP_TO_BIG 1 +#define RADEON_CS_SPACE_FLUSH 2 + +struct radeon_cs { + uint32_t *packets; + unsigned cdw; + unsigned ndw; + unsigned section_ndw; + unsigned section_cdw; +}; + +#define MAX_SPACE_BOS (32) + +struct radeon_cs_manager; + +extern struct radeon_cs *radeon_cs_create(struct radeon_cs_manager *csm, + uint32_t ndw); + +extern int radeon_cs_begin(struct radeon_cs *cs, + uint32_t ndw, + const char *file, + const char *func, int line); +extern int radeon_cs_end(struct radeon_cs *cs, + const char *file, + const char *func, + int line); +extern int radeon_cs_emit(struct radeon_cs *cs); +extern int radeon_cs_destroy(struct radeon_cs *cs); +extern int radeon_cs_erase(struct radeon_cs *cs); +extern int radeon_cs_need_flush(struct radeon_cs *cs); +extern void radeon_cs_print(struct radeon_cs *cs, FILE *file); +extern void radeon_cs_set_limit(struct radeon_cs *cs, uint32_t domain, uint32_t limit); +extern void radeon_cs_space_set_flush(struct radeon_cs *cs, void (*fn)(void *), void *data); +extern int radeon_cs_write_reloc(struct radeon_cs *cs, + struct radeon_bo *bo, + uint32_t read_domain, + uint32_t write_domain, + uint32_t flags); +extern uint32_t radeon_cs_get_id(struct radeon_cs *cs); +/* + * add a persistent BO to the list + * a persistent BO is one that will be referenced across flushes, + * i.e. colorbuffer, textures etc. + * They get reset when a new "operation" happens, where an operation + * is a state emission with a color/textures etc followed by a bunch of vertices. + */ +void radeon_cs_space_add_persistent_bo(struct radeon_cs *cs, + struct radeon_bo *bo, + uint32_t read_domains, + uint32_t write_domain); + +/* reset the persistent BO list */ +void radeon_cs_space_reset_bos(struct radeon_cs *cs); + +/* do a space check with the current persistent BO list */ +int radeon_cs_space_check(struct radeon_cs *cs); + +/* do a space check with the current persistent BO list and a temporary BO + * a temporary BO is like a DMA buffer, which gets flushed with the + * command buffer */ +int radeon_cs_space_check_with_bo(struct radeon_cs *cs, + struct radeon_bo *bo, + uint32_t read_domains, + uint32_t write_domain); + +static inline void radeon_cs_write_dword(struct radeon_cs *cs, uint32_t dword) +{ + cs->packets[cs->cdw++] = dword; + if (cs->section_ndw) { + cs->section_cdw++; + } +} + +static inline void radeon_cs_write_qword(struct radeon_cs *cs, uint64_t qword) +{ + memcpy(cs->packets + cs->cdw, &qword, sizeof(uint64_t)); + cs->cdw += 2; + if (cs->section_ndw) { + cs->section_cdw += 2; + } +} + +static inline void radeon_cs_write_table(struct radeon_cs *cs, + const void *data, uint32_t size) +{ + memcpy(cs->packets + cs->cdw, data, size * 4); + cs->cdw += size; + if (cs->section_ndw) { + cs->section_cdw += size; + } +} +#endif diff --git a/radeon/radeon_cs_gem.c b/radeon/radeon_cs_gem.c new file mode 100644 index 0000000..ef070c6 --- /dev/null +++ b/radeon/radeon_cs_gem.c @@ -0,0 +1,556 @@ +/* + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Aapo Tahkola <aet@rasterburn.org> + * Nicolai Haehnle <prefect_@gmx.net> + * Jérôme Glisse <glisse@freedesktop.org> + */ +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include "radeon_cs.h" +#include "radeon_cs_int.h" +#include "radeon_bo_int.h" +#include "radeon_cs_gem.h" +#include "radeon_bo_gem.h" +#include "drm.h" +#include "libdrm_macros.h" +#include "xf86drm.h" +#include "xf86atomic.h" +#include "radeon_drm.h" + +/* Add LIBDRM_RADEON_BOF_FILES to libdrm_radeon_la_SOURCES when building with BOF_DUMP */ +#define CS_BOF_DUMP 0 +#if CS_BOF_DUMP +#include "bof.h" +#endif + +struct radeon_cs_manager_gem { + struct radeon_cs_manager base; + uint32_t device_id; + unsigned nbof; +}; + +#pragma pack(1) +struct cs_reloc_gem { + uint32_t handle; + uint32_t read_domain; + uint32_t write_domain; + uint32_t flags; +}; + +#pragma pack() +#define RELOC_SIZE (sizeof(struct cs_reloc_gem) / sizeof(uint32_t)) + +struct cs_gem { + struct radeon_cs_int base; + struct drm_radeon_cs cs; + struct drm_radeon_cs_chunk chunks[2]; + unsigned nrelocs; + uint32_t *relocs; + struct radeon_bo_int **relocs_bo; +}; + +static pthread_mutex_t id_mutex = PTHREAD_MUTEX_INITIALIZER; +static uint32_t cs_id_source = 0; + +/** + * result is undefined if called with ~0 + */ +static uint32_t get_first_zero(const uint32_t n) +{ + /* __builtin_ctz returns number of trailing zeros. */ + return 1 << __builtin_ctz(~n); +} + +/** + * Returns a free id for cs. + * If there is no free id we return zero + **/ +static uint32_t generate_id(void) +{ + uint32_t r = 0; + pthread_mutex_lock( &id_mutex ); + /* check for free ids */ + if (cs_id_source != ~r) { + /* find first zero bit */ + r = get_first_zero(cs_id_source); + + /* set id as reserved */ + cs_id_source |= r; + } + pthread_mutex_unlock( &id_mutex ); + return r; +} + +/** + * Free the id for later reuse + **/ +static void free_id(uint32_t id) +{ + pthread_mutex_lock( &id_mutex ); + + cs_id_source &= ~id; + + pthread_mutex_unlock( &id_mutex ); +} + +static struct radeon_cs_int *cs_gem_create(struct radeon_cs_manager *csm, + uint32_t ndw) +{ + struct cs_gem *csg; + + /* max cmd buffer size is 64Kb */ + if (ndw > (64 * 1024 / 4)) { + return NULL; + } + csg = (struct cs_gem*)calloc(1, sizeof(struct cs_gem)); + if (csg == NULL) { + return NULL; + } + csg->base.csm = csm; + csg->base.ndw = 64 * 1024 / 4; + csg->base.packets = (uint32_t*)calloc(1, 64 * 1024); + if (csg->base.packets == NULL) { + free(csg); + return NULL; + } + csg->base.relocs_total_size = 0; + csg->base.crelocs = 0; + csg->base.id = generate_id(); + csg->nrelocs = 4096 / (4 * 4) ; + csg->relocs_bo = (struct radeon_bo_int**)calloc(1, + csg->nrelocs*sizeof(void*)); + if (csg->relocs_bo == NULL) { + free(csg->base.packets); + free(csg); + return NULL; + } + csg->base.relocs = csg->relocs = (uint32_t*)calloc(1, 4096); + if (csg->relocs == NULL) { + free(csg->relocs_bo); + free(csg->base.packets); + free(csg); + return NULL; + } + csg->chunks[0].chunk_id = RADEON_CHUNK_ID_IB; + csg->chunks[0].length_dw = 0; + csg->chunks[0].chunk_data = (uint64_t)(uintptr_t)csg->base.packets; + csg->chunks[1].chunk_id = RADEON_CHUNK_ID_RELOCS; + csg->chunks[1].length_dw = 0; + csg->chunks[1].chunk_data = (uint64_t)(uintptr_t)csg->relocs; + return (struct radeon_cs_int*)csg; +} + +static int cs_gem_write_reloc(struct radeon_cs_int *cs, + struct radeon_bo *bo, + uint32_t read_domain, + uint32_t write_domain, + uint32_t flags) +{ + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + struct cs_gem *csg = (struct cs_gem*)cs; + struct cs_reloc_gem *reloc; + uint32_t idx; + unsigned i; + + assert(boi->space_accounted); + + /* check domains */ + if ((read_domain && write_domain) || (!read_domain && !write_domain)) { + /* in one CS a bo can only be in read or write domain but not + * in read & write domain at the same time + */ + return -EINVAL; + } + if (read_domain == RADEON_GEM_DOMAIN_CPU) { + return -EINVAL; + } + if (write_domain == RADEON_GEM_DOMAIN_CPU) { + return -EINVAL; + } + /* use bit field hash function to determine + if this bo is for sure not in this cs.*/ + if ((atomic_read((atomic_t *)radeon_gem_get_reloc_in_cs(bo)) & cs->id)) { + /* check if bo is already referenced. + * Scanning from end to begin reduces cycles with mesa because + * it often relocates same shared dma bo again. */ + for(i = cs->crelocs; i != 0;) { + --i; + idx = i * RELOC_SIZE; + reloc = (struct cs_reloc_gem*)&csg->relocs[idx]; + if (reloc->handle == bo->handle) { + /* Check domains must be in read or write. As we check already + * checked that in argument one of the read or write domain was + * set we only need to check that if previous reloc as the read + * domain set then the read_domain should also be set for this + * new relocation. + */ + /* the DDX expects to read and write from same pixmap */ + if (write_domain && (reloc->read_domain & write_domain)) { + reloc->read_domain = 0; + reloc->write_domain = write_domain; + } else if (read_domain & reloc->write_domain) { + reloc->read_domain = 0; + } else { + if (write_domain != reloc->write_domain) + return -EINVAL; + if (read_domain != reloc->read_domain) + return -EINVAL; + } + + reloc->read_domain |= read_domain; + reloc->write_domain |= write_domain; + /* update flags */ + reloc->flags |= (flags & reloc->flags); + /* write relocation packet */ + radeon_cs_write_dword((struct radeon_cs *)cs, 0xc0001000); + radeon_cs_write_dword((struct radeon_cs *)cs, idx); + return 0; + } + } + } + /* new relocation */ + if (csg->base.crelocs >= csg->nrelocs) { + /* allocate more memory (TODO: should use a slab allocator maybe) */ + uint32_t *tmp, size; + size = ((csg->nrelocs + 1) * sizeof(struct radeon_bo*)); + tmp = (uint32_t*)realloc(csg->relocs_bo, size); + if (tmp == NULL) { + return -ENOMEM; + } + csg->relocs_bo = (struct radeon_bo_int **)tmp; + size = ((csg->nrelocs + 1) * RELOC_SIZE * 4); + tmp = (uint32_t*)realloc(csg->relocs, size); + if (tmp == NULL) { + return -ENOMEM; + } + cs->relocs = csg->relocs = tmp; + csg->nrelocs += 1; + csg->chunks[1].chunk_data = (uint64_t)(uintptr_t)csg->relocs; + } + csg->relocs_bo[csg->base.crelocs] = boi; + idx = (csg->base.crelocs++) * RELOC_SIZE; + reloc = (struct cs_reloc_gem*)&csg->relocs[idx]; + reloc->handle = bo->handle; + reloc->read_domain = read_domain; + reloc->write_domain = write_domain; + reloc->flags = flags; + csg->chunks[1].length_dw += RELOC_SIZE; + radeon_bo_ref(bo); + /* bo might be referenced from another context so have to use atomic operations */ + atomic_add((atomic_t *)radeon_gem_get_reloc_in_cs(bo), cs->id); + cs->relocs_total_size += boi->size; + radeon_cs_write_dword((struct radeon_cs *)cs, 0xc0001000); + radeon_cs_write_dword((struct radeon_cs *)cs, idx); + return 0; +} + +static int cs_gem_begin(struct radeon_cs_int *cs, + uint32_t ndw, + const char *file, + const char *func, + int line) +{ + + if (cs->section_ndw) { + fprintf(stderr, "CS already in a section(%s,%s,%d)\n", + cs->section_file, cs->section_func, cs->section_line); + fprintf(stderr, "CS can't start section(%s,%s,%d)\n", + file, func, line); + return -EPIPE; + } + cs->section_ndw = ndw; + cs->section_cdw = 0; + cs->section_file = file; + cs->section_func = func; + cs->section_line = line; + + if (cs->cdw + ndw > cs->ndw) { + uint32_t tmp, *ptr; + + /* round up the required size to a multiple of 1024 */ + tmp = (cs->cdw + ndw + 0x3FF) & (~0x3FF); + ptr = (uint32_t*)realloc(cs->packets, 4 * tmp); + if (ptr == NULL) { + return -ENOMEM; + } + cs->packets = ptr; + cs->ndw = tmp; + } + return 0; +} + +static int cs_gem_end(struct radeon_cs_int *cs, + const char *file, + const char *func, + int line) + +{ + if (!cs->section_ndw) { + fprintf(stderr, "CS no section to end at (%s,%s,%d)\n", + file, func, line); + return -EPIPE; + } + if (cs->section_ndw != cs->section_cdw) { + fprintf(stderr, "CS section size mismatch start at (%s,%s,%d) %d vs %d\n", + cs->section_file, cs->section_func, cs->section_line, cs->section_ndw, cs->section_cdw); + fprintf(stderr, "CS section end at (%s,%s,%d)\n", + file, func, line); + + /* We must reset the section even when there is error. */ + cs->section_ndw = 0; + return -EPIPE; + } + cs->section_ndw = 0; + return 0; +} + +#if CS_BOF_DUMP +static void cs_gem_dump_bof(struct radeon_cs_int *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + struct radeon_cs_manager_gem *csm; + bof_t *bcs, *blob, *array, *bo, *size, *handle, *device_id, *root; + char tmp[256]; + unsigned i; + + csm = (struct radeon_cs_manager_gem *)cs->csm; + root = device_id = bcs = blob = array = bo = size = handle = NULL; + root = bof_object(); + if (root == NULL) + goto out_err; + device_id = bof_int32(csm->device_id); + if (device_id == NULL) + return; + if (bof_object_set(root, "device_id", device_id)) + goto out_err; + bof_decref(device_id); + device_id = NULL; + /* dump relocs */ + blob = bof_blob(csg->nrelocs * 16, csg->relocs); + if (blob == NULL) + goto out_err; + if (bof_object_set(root, "reloc", blob)) + goto out_err; + bof_decref(blob); + blob = NULL; + /* dump cs */ + blob = bof_blob(cs->cdw * 4, cs->packets); + if (blob == NULL) + goto out_err; + if (bof_object_set(root, "pm4", blob)) + goto out_err; + bof_decref(blob); + blob = NULL; + /* dump bo */ + array = bof_array(); + if (array == NULL) + goto out_err; + for (i = 0; i < csg->base.crelocs; i++) { + bo = bof_object(); + if (bo == NULL) + goto out_err; + size = bof_int32(csg->relocs_bo[i]->size); + if (size == NULL) + goto out_err; + if (bof_object_set(bo, "size", size)) + goto out_err; + bof_decref(size); + size = NULL; + handle = bof_int32(csg->relocs_bo[i]->handle); + if (handle == NULL) + goto out_err; + if (bof_object_set(bo, "handle", handle)) + goto out_err; + bof_decref(handle); + handle = NULL; + radeon_bo_map((struct radeon_bo*)csg->relocs_bo[i], 0); + blob = bof_blob(csg->relocs_bo[i]->size, csg->relocs_bo[i]->ptr); + radeon_bo_unmap((struct radeon_bo*)csg->relocs_bo[i]); + if (blob == NULL) + goto out_err; + if (bof_object_set(bo, "data", blob)) + goto out_err; + bof_decref(blob); + blob = NULL; + if (bof_array_append(array, bo)) + goto out_err; + bof_decref(bo); + bo = NULL; + } + if (bof_object_set(root, "bo", array)) + goto out_err; + sprintf(tmp, "d-0x%04X-%08d.bof", csm->device_id, csm->nbof++); + bof_dump_file(root, tmp); +out_err: + bof_decref(blob); + bof_decref(array); + bof_decref(bo); + bof_decref(size); + bof_decref(handle); + bof_decref(device_id); + bof_decref(root); +} +#endif + +static int cs_gem_emit(struct radeon_cs_int *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + uint64_t chunk_array[2]; + unsigned i; + int r; + + while (cs->cdw & 7) + radeon_cs_write_dword((struct radeon_cs *)cs, 0x80000000); + +#if CS_BOF_DUMP + cs_gem_dump_bof(cs); +#endif + csg->chunks[0].length_dw = cs->cdw; + + chunk_array[0] = (uint64_t)(uintptr_t)&csg->chunks[0]; + chunk_array[1] = (uint64_t)(uintptr_t)&csg->chunks[1]; + + csg->cs.num_chunks = 2; + csg->cs.chunks = (uint64_t)(uintptr_t)chunk_array; + + r = drmCommandWriteRead(cs->csm->fd, DRM_RADEON_CS, + &csg->cs, sizeof(struct drm_radeon_cs)); + for (i = 0; i < csg->base.crelocs; i++) { + csg->relocs_bo[i]->space_accounted = 0; + /* bo might be referenced from another context so have to use atomic operations */ + atomic_dec((atomic_t *)radeon_gem_get_reloc_in_cs((struct radeon_bo*)csg->relocs_bo[i]), cs->id); + radeon_bo_unref((struct radeon_bo *)csg->relocs_bo[i]); + csg->relocs_bo[i] = NULL; + } + + cs->csm->read_used = 0; + cs->csm->vram_write_used = 0; + cs->csm->gart_write_used = 0; + return r; +} + +static int cs_gem_destroy(struct radeon_cs_int *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + + free_id(cs->id); + free(csg->relocs_bo); + free(cs->relocs); + free(cs->packets); + free(cs); + return 0; +} + +static int cs_gem_erase(struct radeon_cs_int *cs) +{ + struct cs_gem *csg = (struct cs_gem*)cs; + unsigned i; + + if (csg->relocs_bo) { + for (i = 0; i < csg->base.crelocs; i++) { + if (csg->relocs_bo[i]) { + /* bo might be referenced from another context so have to use atomic operations */ + atomic_dec((atomic_t *)radeon_gem_get_reloc_in_cs((struct radeon_bo*)csg->relocs_bo[i]), cs->id); + radeon_bo_unref((struct radeon_bo *)csg->relocs_bo[i]); + csg->relocs_bo[i] = NULL; + } + } + } + cs->relocs_total_size = 0; + cs->cdw = 0; + cs->section_ndw = 0; + cs->crelocs = 0; + csg->chunks[0].length_dw = 0; + csg->chunks[1].length_dw = 0; + return 0; +} + +static int cs_gem_need_flush(struct radeon_cs_int *cs) +{ + return 0; //(cs->relocs_total_size > (32*1024*1024)); +} + +static void cs_gem_print(struct radeon_cs_int *cs, FILE *file) +{ + struct radeon_cs_manager_gem *csm; + unsigned int i; + + csm = (struct radeon_cs_manager_gem *)cs->csm; + fprintf(file, "VENDORID:DEVICEID 0x%04X:0x%04X\n", 0x1002, csm->device_id); + for (i = 0; i < cs->cdw; i++) { + fprintf(file, "0x%08X\n", cs->packets[i]); + } +} + +static const struct radeon_cs_funcs radeon_cs_gem_funcs = { + .cs_create = cs_gem_create, + .cs_write_reloc = cs_gem_write_reloc, + .cs_begin = cs_gem_begin, + .cs_end = cs_gem_end, + .cs_emit = cs_gem_emit, + .cs_destroy = cs_gem_destroy, + .cs_erase = cs_gem_erase, + .cs_need_flush = cs_gem_need_flush, + .cs_print = cs_gem_print, +}; + +static int radeon_get_device_id(int fd, uint32_t *device_id) +{ + struct drm_radeon_info info = {}; + int r; + + *device_id = 0; + info.request = RADEON_INFO_DEVICE_ID; + info.value = (uintptr_t)device_id; + r = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, + sizeof(struct drm_radeon_info)); + return r; +} + +drm_public struct radeon_cs_manager *radeon_cs_manager_gem_ctor(int fd) +{ + struct radeon_cs_manager_gem *csm; + + csm = calloc(1, sizeof(struct radeon_cs_manager_gem)); + if (csm == NULL) { + return NULL; + } + csm->base.funcs = &radeon_cs_gem_funcs; + csm->base.fd = fd; + radeon_get_device_id(fd, &csm->device_id); + return &csm->base; +} + +drm_public void radeon_cs_manager_gem_dtor(struct radeon_cs_manager *csm) +{ + free(csm); +} diff --git a/radeon/radeon_cs_gem.h b/radeon/radeon_cs_gem.h new file mode 100644 index 0000000..5dea38a --- /dev/null +++ b/radeon/radeon_cs_gem.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2008 Nicolai Haehnle + * Copyright © 2008 Jérôme Glisse + * 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. + */ +/* + * Authors: + * Aapo Tahkola <aet@rasterburn.org> + * Nicolai Haehnle <prefect_@gmx.net> + * Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef RADEON_CS_GEM_H +#define RADEON_CS_GEM_H + +#include "radeon_cs.h" + +struct radeon_cs_manager *radeon_cs_manager_gem_ctor(int fd); +void radeon_cs_manager_gem_dtor(struct radeon_cs_manager *csm); + +#endif diff --git a/radeon/radeon_cs_int.h b/radeon/radeon_cs_int.h new file mode 100644 index 0000000..d906ad4 --- /dev/null +++ b/radeon/radeon_cs_int.h @@ -0,0 +1,67 @@ + +#ifndef _RADEON_CS_INT_H_ +#define _RADEON_CS_INT_H_ + +struct radeon_cs_space_check { + struct radeon_bo_int *bo; + uint32_t read_domains; + uint32_t write_domain; + uint32_t new_accounted; +}; + +struct radeon_cs_int { + /* keep first two in same place */ + uint32_t *packets; + unsigned cdw; + unsigned ndw; + unsigned section_ndw; + unsigned section_cdw; + /* private members */ + struct radeon_cs_manager *csm; + void *relocs; + unsigned crelocs; + unsigned relocs_total_size; + const char *section_file; + const char *section_func; + int section_line; + struct radeon_cs_space_check bos[MAX_SPACE_BOS]; + int bo_count; + void (*space_flush_fn)(void *); + void *space_flush_data; + uint32_t id; +}; + +/* cs functions */ +struct radeon_cs_funcs { + struct radeon_cs_int *(*cs_create)(struct radeon_cs_manager *csm, + uint32_t ndw); + int (*cs_write_reloc)(struct radeon_cs_int *cs, + struct radeon_bo *bo, + uint32_t read_domain, + uint32_t write_domain, + uint32_t flags); + int (*cs_begin)(struct radeon_cs_int *cs, + uint32_t ndw, + const char *file, + const char *func, + int line); + int (*cs_end)(struct radeon_cs_int *cs, + const char *file, const char *func, + int line); + + + int (*cs_emit)(struct radeon_cs_int *cs); + int (*cs_destroy)(struct radeon_cs_int *cs); + int (*cs_erase)(struct radeon_cs_int *cs); + int (*cs_need_flush)(struct radeon_cs_int *cs); + void (*cs_print)(struct radeon_cs_int *cs, FILE *file); +}; + +struct radeon_cs_manager { + const struct radeon_cs_funcs *funcs; + int fd; + int32_t vram_limit, gart_limit; + int32_t vram_write_used, gart_write_used; + int32_t read_used; +}; +#endif diff --git a/radeon/radeon_cs_space.c b/radeon/radeon_cs_space.c new file mode 100644 index 0000000..039b041 --- /dev/null +++ b/radeon/radeon_cs_space.c @@ -0,0 +1,248 @@ +/* + * Copyright © 2009 Red Hat 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 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. + */ +/* + */ +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include "libdrm_macros.h" +#include "radeon_cs.h" +#include "radeon_bo_int.h" +#include "radeon_cs_int.h" + +struct rad_sizes { + int32_t op_read; + int32_t op_gart_write; + int32_t op_vram_write; +}; + +static inline int radeon_cs_setup_bo(struct radeon_cs_space_check *sc, struct rad_sizes *sizes) +{ + uint32_t read_domains, write_domain; + struct radeon_bo_int *bo; + + bo = sc->bo; + sc->new_accounted = 0; + read_domains = sc->read_domains; + write_domain = sc->write_domain; + + /* legacy needs a static check */ + if (radeon_bo_is_static((struct radeon_bo *)sc->bo)) { + bo->space_accounted = sc->new_accounted = (read_domains << 16) | write_domain; + return 0; + } + + /* already accounted this bo */ + if (write_domain && (write_domain == bo->space_accounted)) { + sc->new_accounted = bo->space_accounted; + return 0; + } + if (read_domains && ((read_domains << 16) == bo->space_accounted)) { + sc->new_accounted = bo->space_accounted; + return 0; + } + + if (bo->space_accounted == 0) { + if (write_domain) { + if (write_domain == RADEON_GEM_DOMAIN_VRAM) + sizes->op_vram_write += bo->size; + else if (write_domain == RADEON_GEM_DOMAIN_GTT) + sizes->op_gart_write += bo->size; + sc->new_accounted = write_domain; + } else { + sizes->op_read += bo->size; + sc->new_accounted = read_domains << 16; + } + } else { + uint16_t old_read, old_write; + + old_read = bo->space_accounted >> 16; + old_write = bo->space_accounted & 0xffff; + + if (write_domain && (old_read & write_domain)) { + sc->new_accounted = write_domain; + /* moving from read to a write domain */ + if (write_domain == RADEON_GEM_DOMAIN_VRAM) { + sizes->op_read -= bo->size; + sizes->op_vram_write += bo->size; + } else if (write_domain == RADEON_GEM_DOMAIN_GTT) { + sizes->op_read -= bo->size; + sizes->op_gart_write += bo->size; + } + } else if (read_domains & old_write) { + sc->new_accounted = bo->space_accounted & 0xffff; + } else { + /* rewrite the domains */ + if (write_domain != old_write) + fprintf(stderr,"WRITE DOMAIN RELOC FAILURE 0x%x %d %d\n", bo->handle, write_domain, old_write); + if (read_domains != old_read) + fprintf(stderr,"READ DOMAIN RELOC FAILURE 0x%x %d %d\n", bo->handle, read_domains, old_read); + return RADEON_CS_SPACE_FLUSH; + } + } + return 0; +} + +static int radeon_cs_do_space_check(struct radeon_cs_int *cs, struct radeon_cs_space_check *new_tmp) +{ + struct radeon_cs_manager *csm = cs->csm; + int i; + struct radeon_bo_int *bo; + struct rad_sizes sizes; + int ret; + + /* check the totals for this operation */ + + if (cs->bo_count == 0 && !new_tmp) + return 0; + + memset(&sizes, 0, sizeof(struct rad_sizes)); + + /* prepare */ + for (i = 0; i < cs->bo_count; i++) { + ret = radeon_cs_setup_bo(&cs->bos[i], &sizes); + if (ret) + return ret; + } + + if (new_tmp) { + ret = radeon_cs_setup_bo(new_tmp, &sizes); + if (ret) + return ret; + } + + if (sizes.op_read < 0) + sizes.op_read = 0; + + /* check sizes - operation first */ + if ((sizes.op_read + sizes.op_gart_write > csm->gart_limit) || + (sizes.op_vram_write > csm->vram_limit)) { + return RADEON_CS_SPACE_OP_TO_BIG; + } + + if (((csm->vram_write_used + sizes.op_vram_write) > csm->vram_limit) || + ((csm->read_used + csm->gart_write_used + sizes.op_gart_write + sizes.op_read) > csm->gart_limit)) { + return RADEON_CS_SPACE_FLUSH; + } + + csm->gart_write_used += sizes.op_gart_write; + csm->vram_write_used += sizes.op_vram_write; + csm->read_used += sizes.op_read; + /* commit */ + for (i = 0; i < cs->bo_count; i++) { + bo = cs->bos[i].bo; + bo->space_accounted = cs->bos[i].new_accounted; + } + if (new_tmp) + new_tmp->bo->space_accounted = new_tmp->new_accounted; + + return RADEON_CS_SPACE_OK; +} + +drm_public void +radeon_cs_space_add_persistent_bo(struct radeon_cs *cs, struct radeon_bo *bo, + uint32_t read_domains, uint32_t write_domain) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + int i; + for (i = 0; i < csi->bo_count; i++) { + if (csi->bos[i].bo == boi && + csi->bos[i].read_domains == read_domains && + csi->bos[i].write_domain == write_domain) + return; + } + radeon_bo_ref(bo); + i = csi->bo_count; + csi->bos[i].bo = boi; + csi->bos[i].read_domains = read_domains; + csi->bos[i].write_domain = write_domain; + csi->bos[i].new_accounted = 0; + csi->bo_count++; + + assert(csi->bo_count < MAX_SPACE_BOS); +} + +static int radeon_cs_check_space_internal(struct radeon_cs_int *cs, + struct radeon_cs_space_check *tmp_bo) +{ + int ret; + int flushed = 0; + +again: + ret = radeon_cs_do_space_check(cs, tmp_bo); + if (ret == RADEON_CS_SPACE_OP_TO_BIG) + return -1; + if (ret == RADEON_CS_SPACE_FLUSH) { + (*cs->space_flush_fn)(cs->space_flush_data); + if (flushed) + return -1; + flushed = 1; + goto again; + } + return 0; +} + +drm_public int +radeon_cs_space_check_with_bo(struct radeon_cs *cs, struct radeon_bo *bo, + uint32_t read_domains, uint32_t write_domain) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; + struct radeon_cs_space_check temp_bo; + + int ret = 0; + + if (bo) { + temp_bo.bo = boi; + temp_bo.read_domains = read_domains; + temp_bo.write_domain = write_domain; + temp_bo.new_accounted = 0; + } + + ret = radeon_cs_check_space_internal(csi, bo ? &temp_bo : NULL); + return ret; +} + +drm_public int radeon_cs_space_check(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + return radeon_cs_check_space_internal(csi, NULL); +} + +drm_public void radeon_cs_space_reset_bos(struct radeon_cs *cs) +{ + struct radeon_cs_int *csi = (struct radeon_cs_int *)cs; + int i; + for (i = 0; i < csi->bo_count; i++) { + radeon_bo_unref((struct radeon_bo *)csi->bos[i].bo); + csi->bos[i].bo = NULL; + csi->bos[i].read_domains = 0; + csi->bos[i].write_domain = 0; + csi->bos[i].new_accounted = 0; + } + csi->bo_count = 0; +} diff --git a/radeon/radeon_surface.c b/radeon/radeon_surface.c new file mode 100644 index 0000000..0ff53a3 --- /dev/null +++ b/radeon/radeon_surface.c @@ -0,0 +1,2567 @@ +/* + * Copyright © 2011 Red Hat 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. + */ +/* + * Authors: + * Jérôme Glisse <jglisse@redhat.com> + */ +#include <stdbool.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include "drm.h" +#include "libdrm_macros.h" +#include "xf86drm.h" +#include "radeon_drm.h" +#include "radeon_surface.h" + +#define CIK_TILE_MODE_COLOR_2D 14 +#define CIK_TILE_MODE_COLOR_2D_SCANOUT 10 +#define CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_64 0 +#define CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_128 1 +#define CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_256 2 +#define CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_512 3 +#define CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_ROW_SIZE 4 + +#define ALIGN(value, alignment) (((value) + alignment - 1) & ~(alignment - 1)) +#define MAX2(A, B) ((A) > (B) ? (A) : (B)) +#define MIN2(A, B) ((A) < (B) ? (A) : (B)) + +/* keep this private */ +enum radeon_family { + CHIP_UNKNOWN, + CHIP_R600, + CHIP_RV610, + CHIP_RV630, + CHIP_RV670, + CHIP_RV620, + CHIP_RV635, + CHIP_RS780, + CHIP_RS880, + CHIP_RV770, + CHIP_RV730, + CHIP_RV710, + CHIP_RV740, + CHIP_CEDAR, + CHIP_REDWOOD, + CHIP_JUNIPER, + CHIP_CYPRESS, + CHIP_HEMLOCK, + CHIP_PALM, + CHIP_SUMO, + CHIP_SUMO2, + CHIP_BARTS, + CHIP_TURKS, + CHIP_CAICOS, + CHIP_CAYMAN, + CHIP_ARUBA, + CHIP_TAHITI, + CHIP_PITCAIRN, + CHIP_VERDE, + CHIP_OLAND, + CHIP_HAINAN, + CHIP_BONAIRE, + CHIP_KAVERI, + CHIP_KABINI, + CHIP_HAWAII, + CHIP_MULLINS, + CHIP_LAST, +}; + +typedef int (*hw_init_surface_t)(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf); +typedef int (*hw_best_surface_t)(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf); + +struct radeon_hw_info { + /* apply to r6, eg */ + uint32_t group_bytes; + uint32_t num_banks; + uint32_t num_pipes; + /* apply to eg */ + uint32_t row_size; + unsigned allow_2d; + /* apply to si */ + uint32_t tile_mode_array[32]; + /* apply to cik */ + uint32_t macrotile_mode_array[16]; +}; + +struct radeon_surface_manager { + int fd; + uint32_t device_id; + struct radeon_hw_info hw_info; + unsigned family; + hw_init_surface_t surface_init; + hw_best_surface_t surface_best; +}; + +/* helper */ +static int radeon_get_value(int fd, unsigned req, uint32_t *value) +{ + struct drm_radeon_info info = {}; + int r; + + *value = 0; + info.request = req; + info.value = (uintptr_t)value; + r = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, + sizeof(struct drm_radeon_info)); + return r; +} + +static int radeon_get_family(struct radeon_surface_manager *surf_man) +{ + switch (surf_man->device_id) { +#define CHIPSET(pci_id, name, fam) case pci_id: surf_man->family = CHIP_##fam; break; +#include "r600_pci_ids.h" +#undef CHIPSET + default: + return -EINVAL; + } + return 0; +} + +static unsigned next_power_of_two(unsigned x) +{ + if (x <= 1) + return 1; + + return (1 << ((sizeof(unsigned) * 8) - __builtin_clz(x - 1))); +} + +static unsigned mip_minify(unsigned size, unsigned level) +{ + unsigned val; + + val = MAX2(1, size >> level); + if (level > 0) + val = next_power_of_two(val); + return val; +} + +static void surf_minify(struct radeon_surface *surf, + struct radeon_surface_level *surflevel, + unsigned bpe, unsigned level, + uint32_t xalign, uint32_t yalign, uint32_t zalign, + uint64_t offset) +{ + surflevel->npix_x = mip_minify(surf->npix_x, level); + surflevel->npix_y = mip_minify(surf->npix_y, level); + surflevel->npix_z = mip_minify(surf->npix_z, level); + surflevel->nblk_x = (surflevel->npix_x + surf->blk_w - 1) / surf->blk_w; + surflevel->nblk_y = (surflevel->npix_y + surf->blk_h - 1) / surf->blk_h; + surflevel->nblk_z = (surflevel->npix_z + surf->blk_d - 1) / surf->blk_d; + if (surf->nsamples == 1 && surflevel->mode == RADEON_SURF_MODE_2D && + !(surf->flags & RADEON_SURF_FMASK)) { + if (surflevel->nblk_x < xalign || surflevel->nblk_y < yalign) { + surflevel->mode = RADEON_SURF_MODE_1D; + return; + } + } + surflevel->nblk_x = ALIGN(surflevel->nblk_x, xalign); + surflevel->nblk_y = ALIGN(surflevel->nblk_y, yalign); + surflevel->nblk_z = ALIGN(surflevel->nblk_z, zalign); + + surflevel->offset = offset; + surflevel->pitch_bytes = surflevel->nblk_x * bpe * surf->nsamples; + surflevel->slice_size = (uint64_t)surflevel->pitch_bytes * surflevel->nblk_y; + + surf->bo_size = offset + surflevel->slice_size * surflevel->nblk_z * surf->array_size; +} + +/* =========================================================================== + * r600/r700 family + */ +static int r6_init_hw_info(struct radeon_surface_manager *surf_man) +{ + uint32_t tiling_config; + drmVersionPtr version; + int r; + + r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG, + &tiling_config); + if (r) { + return r; + } + + surf_man->hw_info.allow_2d = 0; + version = drmGetVersion(surf_man->fd); + if (version && version->version_minor >= 14) { + surf_man->hw_info.allow_2d = 1; + } + drmFreeVersion(version); + + switch ((tiling_config & 0xe) >> 1) { + case 0: + surf_man->hw_info.num_pipes = 1; + break; + case 1: + surf_man->hw_info.num_pipes = 2; + break; + case 2: + surf_man->hw_info.num_pipes = 4; + break; + case 3: + surf_man->hw_info.num_pipes = 8; + break; + default: + surf_man->hw_info.num_pipes = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0x30) >> 4) { + case 0: + surf_man->hw_info.num_banks = 4; + break; + case 1: + surf_man->hw_info.num_banks = 8; + break; + default: + surf_man->hw_info.num_banks = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xc0) >> 6) { + case 0: + surf_man->hw_info.group_bytes = 256; + break; + case 1: + surf_man->hw_info.group_bytes = 512; + break; + default: + surf_man->hw_info.group_bytes = 256; + surf_man->hw_info.allow_2d = 0; + break; + } + return 0; +} + +static int r6_surface_init_linear(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign; + unsigned i; + + /* compute alignment */ + if (!start_level) { + surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes); + } + /* the 32 alignment is for scanout, cb or db but to allow texture to be + * easily bound as such we force this alignment to all surface + */ + xalign = MAX2(1, surf_man->hw_info.group_bytes / surf->bpe); + yalign = 1; + zalign = 1; + if (surf->flags & RADEON_SURF_SCANOUT) { + xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign); + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + surf->level[i].mode = RADEON_SURF_MODE_LINEAR; + surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign, zalign, offset); + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + } + return 0; +} + +static int r6_surface_init_linear_aligned(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign; + unsigned i; + + /* compute alignment */ + if (!start_level) { + surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes); + } + xalign = MAX2(64, surf_man->hw_info.group_bytes / surf->bpe); + yalign = 1; + zalign = 1; + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED; + surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign, zalign, offset); + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + } + return 0; +} + +static int r6_surface_init_1d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign, tilew; + unsigned i; + + /* compute alignment */ + tilew = 8; + xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples); + xalign = MAX2(tilew, xalign); + yalign = tilew; + zalign = 1; + if (surf->flags & RADEON_SURF_SCANOUT) { + xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign); + } + if (!start_level) { + surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes); + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + surf->level[i].mode = RADEON_SURF_MODE_1D; + surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign, zalign, offset); + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + } + return 0; +} + +static int r6_surface_init_2d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign, tilew; + unsigned i; + + /* compute alignment */ + tilew = 8; + zalign = 1; + xalign = (surf_man->hw_info.group_bytes * surf_man->hw_info.num_banks) / + (tilew * surf->bpe * surf->nsamples); + xalign = MAX2(tilew * surf_man->hw_info.num_banks, xalign); + if (surf->flags & RADEON_SURF_FMASK) + xalign = MAX2(128, xalign); + yalign = tilew * surf_man->hw_info.num_pipes; + if (surf->flags & RADEON_SURF_SCANOUT) { + xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign); + } + if (!start_level) { + surf->bo_alignment = + MAX2(surf_man->hw_info.num_pipes * + surf_man->hw_info.num_banks * + surf->nsamples * surf->bpe * 64, + xalign * yalign * surf->nsamples * surf->bpe); + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + surf->level[i].mode = RADEON_SURF_MODE_2D; + surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign, zalign, offset); + if (surf->level[i].mode == RADEON_SURF_MODE_1D) { + return r6_surface_init_1d(surf_man, surf, offset, i); + } + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + } + return 0; +} + +static int r6_surface_init(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode; + int r; + + /* MSAA surfaces support the 2D mode only. */ + if (surf->nsamples > 1) { + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE); + } + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) { + /* zbuffer only support 1D or 2D tiled surface */ + switch (mode) { + case RADEON_SURF_MODE_1D: + case RADEON_SURF_MODE_2D: + break; + default: + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE); + break; + } + } + + /* force 1d on kernel that can't do 2d */ + if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) { + if (surf->nsamples > 1) { + fprintf(stderr, "radeon: Cannot use 2D tiling for an MSAA surface (%i).\n", __LINE__); + return -EFAULT; + } + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(mode, MODE); + } + + /* check surface dimension */ + if (surf->npix_x > 8192 || surf->npix_y > 8192 || surf->npix_z > 8192) { + return -EINVAL; + } + + /* check mipmap last_level */ + if (surf->last_level > 14) { + return -EINVAL; + } + + /* check tiling mode */ + switch (mode) { + case RADEON_SURF_MODE_LINEAR: + r = r6_surface_init_linear(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_LINEAR_ALIGNED: + r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_1D: + r = r6_surface_init_1d(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_2D: + r = r6_surface_init_2d(surf_man, surf, 0, 0); + break; + default: + return -EINVAL; + } + return r; +} + +static int r6_surface_best(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + /* no value to optimize for r6xx/r7xx */ + return 0; +} + + +/* =========================================================================== + * evergreen family + */ +static int eg_init_hw_info(struct radeon_surface_manager *surf_man) +{ + uint32_t tiling_config; + drmVersionPtr version; + int r; + + r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG, + &tiling_config); + if (r) { + return r; + } + + surf_man->hw_info.allow_2d = 0; + version = drmGetVersion(surf_man->fd); + if (version && version->version_minor >= 16) { + surf_man->hw_info.allow_2d = 1; + } + drmFreeVersion(version); + + switch (tiling_config & 0xf) { + case 0: + surf_man->hw_info.num_pipes = 1; + break; + case 1: + surf_man->hw_info.num_pipes = 2; + break; + case 2: + surf_man->hw_info.num_pipes = 4; + break; + case 3: + surf_man->hw_info.num_pipes = 8; + break; + default: + surf_man->hw_info.num_pipes = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf0) >> 4) { + case 0: + surf_man->hw_info.num_banks = 4; + break; + case 1: + surf_man->hw_info.num_banks = 8; + break; + case 2: + surf_man->hw_info.num_banks = 16; + break; + default: + surf_man->hw_info.num_banks = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf00) >> 8) { + case 0: + surf_man->hw_info.group_bytes = 256; + break; + case 1: + surf_man->hw_info.group_bytes = 512; + break; + default: + surf_man->hw_info.group_bytes = 256; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf000) >> 12) { + case 0: + surf_man->hw_info.row_size = 1024; + break; + case 1: + surf_man->hw_info.row_size = 2048; + break; + case 2: + surf_man->hw_info.row_size = 4096; + break; + default: + surf_man->hw_info.row_size = 4096; + surf_man->hw_info.allow_2d = 0; + break; + } + return 0; +} + +static void eg_surf_minify(struct radeon_surface *surf, + struct radeon_surface_level *surflevel, + unsigned bpe, + unsigned level, + unsigned slice_pt, + unsigned mtilew, + unsigned mtileh, + unsigned mtileb, + uint64_t offset) +{ + unsigned mtile_pr, mtile_ps; + + surflevel->npix_x = mip_minify(surf->npix_x, level); + surflevel->npix_y = mip_minify(surf->npix_y, level); + surflevel->npix_z = mip_minify(surf->npix_z, level); + surflevel->nblk_x = (surflevel->npix_x + surf->blk_w - 1) / surf->blk_w; + surflevel->nblk_y = (surflevel->npix_y + surf->blk_h - 1) / surf->blk_h; + surflevel->nblk_z = (surflevel->npix_z + surf->blk_d - 1) / surf->blk_d; + if (surf->nsamples == 1 && surflevel->mode == RADEON_SURF_MODE_2D && + !(surf->flags & RADEON_SURF_FMASK)) { + if (surflevel->nblk_x < mtilew || surflevel->nblk_y < mtileh) { + surflevel->mode = RADEON_SURF_MODE_1D; + return; + } + } + surflevel->nblk_x = ALIGN(surflevel->nblk_x, mtilew); + surflevel->nblk_y = ALIGN(surflevel->nblk_y, mtileh); + surflevel->nblk_z = ALIGN(surflevel->nblk_z, 1); + + /* macro tile per row */ + mtile_pr = surflevel->nblk_x / mtilew; + /* macro tile per slice */ + mtile_ps = (mtile_pr * surflevel->nblk_y) / mtileh; + + surflevel->offset = offset; + surflevel->pitch_bytes = surflevel->nblk_x * bpe * surf->nsamples; + surflevel->slice_size = (uint64_t)mtile_ps * mtileb * slice_pt; + + surf->bo_size = offset + surflevel->slice_size * surflevel->nblk_z * surf->array_size; +} + +static int eg_surface_init_1d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + struct radeon_surface_level *level, + unsigned bpe, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign, tilew; + unsigned i; + + /* compute alignment */ + tilew = 8; + xalign = surf_man->hw_info.group_bytes / (tilew * bpe * surf->nsamples); + xalign = MAX2(tilew, xalign); + yalign = tilew; + zalign = 1; + if (surf->flags & RADEON_SURF_SCANOUT) { + xalign = MAX2((bpe == 1) ? 64 : 32, xalign); + } + + if (!start_level) { + unsigned alignment = MAX2(256, surf_man->hw_info.group_bytes); + surf->bo_alignment = MAX2(surf->bo_alignment, alignment); + + if (offset) { + offset = ALIGN(offset, alignment); + } + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + level[i].mode = RADEON_SURF_MODE_1D; + surf_minify(surf, level+i, bpe, i, xalign, yalign, zalign, offset); + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + } + return 0; +} + +static int eg_surface_init_2d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + struct radeon_surface_level *level, + unsigned bpe, unsigned tile_split, + uint64_t offset, unsigned start_level) +{ + unsigned tilew, tileh, tileb; + unsigned mtilew, mtileh, mtileb; + unsigned slice_pt; + unsigned i; + + /* compute tile values */ + tilew = 8; + tileh = 8; + tileb = tilew * tileh * bpe * surf->nsamples; + /* slices per tile */ + slice_pt = 1; + if (tileb > tile_split && tile_split) { + slice_pt = tileb / tile_split; + } + tileb = tileb / slice_pt; + + /* macro tile width & height */ + mtilew = (tilew * surf->bankw * surf_man->hw_info.num_pipes) * surf->mtilea; + mtileh = (tileh * surf->bankh * surf_man->hw_info.num_banks) / surf->mtilea; + /* macro tile bytes */ + mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb; + + if (!start_level) { + unsigned alignment = MAX2(256, mtileb); + surf->bo_alignment = MAX2(surf->bo_alignment, alignment); + + if (offset) { + offset = ALIGN(offset, alignment); + } + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + level[i].mode = RADEON_SURF_MODE_2D; + eg_surf_minify(surf, level+i, bpe, i, slice_pt, mtilew, mtileh, mtileb, offset); + if (level[i].mode == RADEON_SURF_MODE_1D) { + return eg_surface_init_1d(surf_man, surf, level, bpe, offset, i); + } + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + } + return 0; +} + +static int eg_surface_sanity(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned mode) +{ + unsigned tileb; + + /* check surface dimension */ + if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) { + return -EINVAL; + } + + /* check mipmap last_level */ + if (surf->last_level > 15) { + return -EINVAL; + } + + /* force 1d on kernel that can't do 2d */ + if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) { + if (surf->nsamples > 1) { + fprintf(stderr, "radeon: Cannot use 2D tiling for an MSAA surface (%i).\n", __LINE__); + return -EFAULT; + } + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(mode, MODE); + } + + /* check tile split */ + if (mode == RADEON_SURF_MODE_2D) { + switch (surf->tile_split) { + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + case 4096: + break; + default: + return -EINVAL; + } + switch (surf->mtilea) { + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + /* check aspect ratio */ + if (surf_man->hw_info.num_banks < surf->mtilea) { + return -EINVAL; + } + /* check bank width */ + switch (surf->bankw) { + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + /* check bank height */ + switch (surf->bankh) { + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples); + if ((tileb * surf->bankh * surf->bankw) < surf_man->hw_info.group_bytes) { + return -EINVAL; + } + } + + return 0; +} + +static int eg_surface_init_1d_miptrees(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned zs_flags = RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER; + int r, is_depth_stencil = (surf->flags & zs_flags) == zs_flags; + /* Old libdrm_macros.headers didn't have stencil_level in it. This prevents crashes. */ + struct radeon_surface_level tmp[RADEON_SURF_MAX_LEVEL]; + struct radeon_surface_level *stencil_level = + (surf->flags & RADEON_SURF_HAS_SBUFFER_MIPTREE) ? surf->stencil_level : tmp; + + r = eg_surface_init_1d(surf_man, surf, surf->level, surf->bpe, 0, 0); + if (r) + return r; + + if (is_depth_stencil) { + r = eg_surface_init_1d(surf_man, surf, stencil_level, 1, + surf->bo_size, 0); + surf->stencil_offset = stencil_level[0].offset; + } + return r; +} + +static int eg_surface_init_2d_miptrees(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned zs_flags = RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER; + int r, is_depth_stencil = (surf->flags & zs_flags) == zs_flags; + /* Old libdrm_macros.headers didn't have stencil_level in it. This prevents crashes. */ + struct radeon_surface_level tmp[RADEON_SURF_MAX_LEVEL]; + struct radeon_surface_level *stencil_level = + (surf->flags & RADEON_SURF_HAS_SBUFFER_MIPTREE) ? surf->stencil_level : tmp; + + r = eg_surface_init_2d(surf_man, surf, surf->level, surf->bpe, + surf->tile_split, 0, 0); + if (r) + return r; + + if (is_depth_stencil) { + r = eg_surface_init_2d(surf_man, surf, stencil_level, 1, + surf->stencil_tile_split, surf->bo_size, 0); + surf->stencil_offset = stencil_level[0].offset; + } + return r; +} + +static int eg_surface_init(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode; + int r; + + /* MSAA surfaces support the 2D mode only. */ + if (surf->nsamples > 1) { + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE); + } + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) { + /* zbuffer only support 1D or 2D tiled surface */ + switch (mode) { + case RADEON_SURF_MODE_1D: + case RADEON_SURF_MODE_2D: + break; + default: + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE); + break; + } + } + + r = eg_surface_sanity(surf_man, surf, mode); + if (r) { + return r; + } + + surf->stencil_offset = 0; + surf->bo_alignment = 0; + + /* check tiling mode */ + switch (mode) { + case RADEON_SURF_MODE_LINEAR: + r = r6_surface_init_linear(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_LINEAR_ALIGNED: + r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_1D: + r = eg_surface_init_1d_miptrees(surf_man, surf); + break; + case RADEON_SURF_MODE_2D: + r = eg_surface_init_2d_miptrees(surf_man, surf); + break; + default: + return -EINVAL; + } + return r; +} + +static unsigned log2_int(unsigned x) +{ + unsigned l; + + if (x < 2) { + return 0; + } + for (l = 2; ; l++) { + if ((unsigned)(1 << l) > x) { + return l - 1; + } + } + return 0; +} + +/* compute best tile_split, bankw, bankh, mtilea + * depending on surface + */ +static int eg_surface_best(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, tileb, h_over_w; + int r; + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + /* set some default value to avoid sanity check choking on them */ + surf->tile_split = 1024; + surf->bankw = 1; + surf->bankh = 1; + surf->mtilea = surf_man->hw_info.num_banks; + tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples); + for (; surf->bankh <= 8; surf->bankh *= 2) { + if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) { + break; + } + } + if (surf->mtilea > 8) { + surf->mtilea = 8; + } + + r = eg_surface_sanity(surf_man, surf, mode); + if (r) { + return r; + } + + if (mode != RADEON_SURF_MODE_2D) { + /* nothing to do for non 2D tiled surface */ + return 0; + } + + /* Tweak TILE_SPLIT for performance here. */ + if (surf->nsamples > 1) { + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) { + switch (surf->nsamples) { + case 2: + surf->tile_split = 128; + break; + case 4: + surf->tile_split = 128; + break; + case 8: + surf->tile_split = 256; + break; + case 16: /* cayman only */ + surf->tile_split = 512; + break; + default: + fprintf(stderr, "radeon: Wrong number of samples %i (%i)\n", + surf->nsamples, __LINE__); + return -EINVAL; + } + surf->stencil_tile_split = 64; + } else { + /* tile split must be >= 256 for colorbuffer surfaces, + * SAMPLE_SPLIT = tile_split / (bpe * 64), the optimal value is 2 + */ + surf->tile_split = MAX2(2 * surf->bpe * 64, 256); + if (surf->tile_split > 4096) + surf->tile_split = 4096; + } + } else { + /* set tile split to row size */ + surf->tile_split = surf_man->hw_info.row_size; + surf->stencil_tile_split = surf_man->hw_info.row_size / 2; + } + + /* bankw or bankh greater than 1 increase alignment requirement, not + * sure if it's worth using smaller bankw & bankh to stick with 2D + * tiling on small surface rather than falling back to 1D tiling. + * Use recommended value based on tile size for now. + * + * fmask buffer has different optimal value figure them out once we + * use it. + */ + if (surf->flags & RADEON_SURF_SBUFFER) { + /* assume 1 bytes for stencil, we optimize for stencil as stencil + * and depth shares surface values + */ + tileb = MIN2(surf->tile_split, 64 * surf->nsamples); + } else { + tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples); + } + + /* use bankw of 1 to minimize width alignment, might be interesting to + * increase it for large surface + */ + surf->bankw = 1; + switch (tileb) { + case 64: + surf->bankh = 4; + break; + case 128: + case 256: + surf->bankh = 2; + break; + default: + surf->bankh = 1; + break; + } + /* double check the constraint */ + for (; surf->bankh <= 8; surf->bankh *= 2) { + if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) { + break; + } + } + + h_over_w = (((surf->bankh * surf_man->hw_info.num_banks) << 16) / + (surf->bankw * surf_man->hw_info.num_pipes)) >> 16; + surf->mtilea = 1 << (log2_int(h_over_w) >> 1); + + return 0; +} + + +/* =========================================================================== + * Southern Islands family + */ +#define SI__GB_TILE_MODE__PIPE_CONFIG(x) (((x) >> 6) & 0x1f) +#define SI__PIPE_CONFIG__ADDR_SURF_P2 0 +#define SI__PIPE_CONFIG__ADDR_SURF_P4_8x16 4 +#define SI__PIPE_CONFIG__ADDR_SURF_P4_16x16 5 +#define SI__PIPE_CONFIG__ADDR_SURF_P4_16x32 6 +#define SI__PIPE_CONFIG__ADDR_SURF_P4_32x32 7 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_16x16_8x16 8 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_8x16 9 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_8x16 10 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_16x16 11 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x16 12 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x32 13 +#define SI__PIPE_CONFIG__ADDR_SURF_P8_32x64_32x32 14 +#define SI__GB_TILE_MODE__TILE_SPLIT(x) (((x) >> 11) & 0x7) +#define SI__TILE_SPLIT__64B 0 +#define SI__TILE_SPLIT__128B 1 +#define SI__TILE_SPLIT__256B 2 +#define SI__TILE_SPLIT__512B 3 +#define SI__TILE_SPLIT__1024B 4 +#define SI__TILE_SPLIT__2048B 5 +#define SI__TILE_SPLIT__4096B 6 +#define SI__GB_TILE_MODE__BANK_WIDTH(x) (((x) >> 14) & 0x3) +#define SI__BANK_WIDTH__1 0 +#define SI__BANK_WIDTH__2 1 +#define SI__BANK_WIDTH__4 2 +#define SI__BANK_WIDTH__8 3 +#define SI__GB_TILE_MODE__BANK_HEIGHT(x) (((x) >> 16) & 0x3) +#define SI__BANK_HEIGHT__1 0 +#define SI__BANK_HEIGHT__2 1 +#define SI__BANK_HEIGHT__4 2 +#define SI__BANK_HEIGHT__8 3 +#define SI__GB_TILE_MODE__MACRO_TILE_ASPECT(x) (((x) >> 18) & 0x3) +#define SI__MACRO_TILE_ASPECT__1 0 +#define SI__MACRO_TILE_ASPECT__2 1 +#define SI__MACRO_TILE_ASPECT__4 2 +#define SI__MACRO_TILE_ASPECT__8 3 +#define SI__GB_TILE_MODE__NUM_BANKS(x) (((x) >> 20) & 0x3) +#define SI__NUM_BANKS__2_BANK 0 +#define SI__NUM_BANKS__4_BANK 1 +#define SI__NUM_BANKS__8_BANK 2 +#define SI__NUM_BANKS__16_BANK 3 + + +static void si_gb_tile_mode(uint32_t gb_tile_mode, + unsigned *num_pipes, + unsigned *num_banks, + uint32_t *macro_tile_aspect, + uint32_t *bank_w, + uint32_t *bank_h, + uint32_t *tile_split) +{ + if (num_pipes) { + switch (SI__GB_TILE_MODE__PIPE_CONFIG(gb_tile_mode)) { + case SI__PIPE_CONFIG__ADDR_SURF_P2: + default: + *num_pipes = 2; + break; + case SI__PIPE_CONFIG__ADDR_SURF_P4_8x16: + case SI__PIPE_CONFIG__ADDR_SURF_P4_16x16: + case SI__PIPE_CONFIG__ADDR_SURF_P4_16x32: + case SI__PIPE_CONFIG__ADDR_SURF_P4_32x32: + *num_pipes = 4; + break; + case SI__PIPE_CONFIG__ADDR_SURF_P8_16x16_8x16: + case SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_8x16: + case SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_8x16: + case SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_16x16: + case SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x16: + case SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x32: + case SI__PIPE_CONFIG__ADDR_SURF_P8_32x64_32x32: + *num_pipes = 8; + break; + } + } + if (num_banks) { + switch (SI__GB_TILE_MODE__NUM_BANKS(gb_tile_mode)) { + default: + case SI__NUM_BANKS__2_BANK: + *num_banks = 2; + break; + case SI__NUM_BANKS__4_BANK: + *num_banks = 4; + break; + case SI__NUM_BANKS__8_BANK: + *num_banks = 8; + break; + case SI__NUM_BANKS__16_BANK: + *num_banks = 16; + break; + } + } + if (macro_tile_aspect) { + switch (SI__GB_TILE_MODE__MACRO_TILE_ASPECT(gb_tile_mode)) { + default: + case SI__MACRO_TILE_ASPECT__1: + *macro_tile_aspect = 1; + break; + case SI__MACRO_TILE_ASPECT__2: + *macro_tile_aspect = 2; + break; + case SI__MACRO_TILE_ASPECT__4: + *macro_tile_aspect = 4; + break; + case SI__MACRO_TILE_ASPECT__8: + *macro_tile_aspect = 8; + break; + } + } + if (bank_w) { + switch (SI__GB_TILE_MODE__BANK_WIDTH(gb_tile_mode)) { + default: + case SI__BANK_WIDTH__1: + *bank_w = 1; + break; + case SI__BANK_WIDTH__2: + *bank_w = 2; + break; + case SI__BANK_WIDTH__4: + *bank_w = 4; + break; + case SI__BANK_WIDTH__8: + *bank_w = 8; + break; + } + } + if (bank_h) { + switch (SI__GB_TILE_MODE__BANK_HEIGHT(gb_tile_mode)) { + default: + case SI__BANK_HEIGHT__1: + *bank_h = 1; + break; + case SI__BANK_HEIGHT__2: + *bank_h = 2; + break; + case SI__BANK_HEIGHT__4: + *bank_h = 4; + break; + case SI__BANK_HEIGHT__8: + *bank_h = 8; + break; + } + } + if (tile_split) { + switch (SI__GB_TILE_MODE__TILE_SPLIT(gb_tile_mode)) { + default: + case SI__TILE_SPLIT__64B: + *tile_split = 64; + break; + case SI__TILE_SPLIT__128B: + *tile_split = 128; + break; + case SI__TILE_SPLIT__256B: + *tile_split = 256; + break; + case SI__TILE_SPLIT__512B: + *tile_split = 512; + break; + case SI__TILE_SPLIT__1024B: + *tile_split = 1024; + break; + case SI__TILE_SPLIT__2048B: + *tile_split = 2048; + break; + case SI__TILE_SPLIT__4096B: + *tile_split = 4096; + break; + } + } +} + +static int si_init_hw_info(struct radeon_surface_manager *surf_man) +{ + uint32_t tiling_config; + drmVersionPtr version; + int r; + + r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG, + &tiling_config); + if (r) { + return r; + } + + surf_man->hw_info.allow_2d = 0; + version = drmGetVersion(surf_man->fd); + if (version && version->version_minor >= 33) { + if (!radeon_get_value(surf_man->fd, RADEON_INFO_SI_TILE_MODE_ARRAY, surf_man->hw_info.tile_mode_array)) { + surf_man->hw_info.allow_2d = 1; + } + } + drmFreeVersion(version); + + switch (tiling_config & 0xf) { + case 0: + surf_man->hw_info.num_pipes = 1; + break; + case 1: + surf_man->hw_info.num_pipes = 2; + break; + case 2: + surf_man->hw_info.num_pipes = 4; + break; + case 3: + surf_man->hw_info.num_pipes = 8; + break; + default: + surf_man->hw_info.num_pipes = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf0) >> 4) { + case 0: + surf_man->hw_info.num_banks = 4; + break; + case 1: + surf_man->hw_info.num_banks = 8; + break; + case 2: + surf_man->hw_info.num_banks = 16; + break; + default: + surf_man->hw_info.num_banks = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf00) >> 8) { + case 0: + surf_man->hw_info.group_bytes = 256; + break; + case 1: + surf_man->hw_info.group_bytes = 512; + break; + default: + surf_man->hw_info.group_bytes = 256; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf000) >> 12) { + case 0: + surf_man->hw_info.row_size = 1024; + break; + case 1: + surf_man->hw_info.row_size = 2048; + break; + case 2: + surf_man->hw_info.row_size = 4096; + break; + default: + surf_man->hw_info.row_size = 4096; + surf_man->hw_info.allow_2d = 0; + break; + } + return 0; +} + +static int si_surface_sanity(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned mode, unsigned *tile_mode, unsigned *stencil_tile_mode) +{ + uint32_t gb_tile_mode; + + /* check surface dimension */ + if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) { + return -EINVAL; + } + + /* check mipmap last_level */ + if (surf->last_level > 15) { + return -EINVAL; + } + + /* force 1d on kernel that can't do 2d */ + if (mode > RADEON_SURF_MODE_1D && + (!surf_man->hw_info.allow_2d || !(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX))) { + if (surf->nsamples > 1) { + fprintf(stderr, "radeon: Cannot use 1D tiling for an MSAA surface (%i).\n", __LINE__); + return -EFAULT; + } + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(mode, MODE); + } + + if (surf->nsamples > 1 && mode != RADEON_SURF_MODE_2D) { + return -EINVAL; + } + + if (!surf->tile_split) { + /* default value */ + surf->mtilea = 1; + surf->bankw = 1; + surf->bankh = 1; + surf->tile_split = 64; + surf->stencil_tile_split = 64; + } + + switch (mode) { + case RADEON_SURF_MODE_2D: + if (surf->flags & RADEON_SURF_SBUFFER) { + switch (surf->nsamples) { + case 1: + *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D; + break; + case 2: + *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_2AA; + break; + case 4: + *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_4AA; + break; + case 8: + *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_8AA; + break; + default: + return -EINVAL; + } + /* retrieve tiling mode value */ + gb_tile_mode = surf_man->hw_info.tile_mode_array[*stencil_tile_mode]; + si_gb_tile_mode(gb_tile_mode, NULL, NULL, NULL, NULL, NULL, &surf->stencil_tile_split); + } + if (surf->flags & RADEON_SURF_ZBUFFER) { + switch (surf->nsamples) { + case 1: + *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D; + break; + case 2: + *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_2AA; + break; + case 4: + *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_4AA; + break; + case 8: + *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_8AA; + break; + default: + return -EINVAL; + } + } else if (surf->flags & RADEON_SURF_SCANOUT) { + switch (surf->bpe) { + case 2: + *tile_mode = SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP; + break; + case 4: + *tile_mode = SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP; + break; + default: + return -EINVAL; + } + } else { + switch (surf->bpe) { + case 1: + *tile_mode = SI_TILE_MODE_COLOR_2D_8BPP; + break; + case 2: + *tile_mode = SI_TILE_MODE_COLOR_2D_16BPP; + break; + case 4: + *tile_mode = SI_TILE_MODE_COLOR_2D_32BPP; + break; + case 8: + case 16: + *tile_mode = SI_TILE_MODE_COLOR_2D_64BPP; + break; + default: + return -EINVAL; + } + } + /* retrieve tiling mode value */ + gb_tile_mode = surf_man->hw_info.tile_mode_array[*tile_mode]; + si_gb_tile_mode(gb_tile_mode, NULL, NULL, &surf->mtilea, &surf->bankw, &surf->bankh, &surf->tile_split); + break; + case RADEON_SURF_MODE_1D: + if (surf->flags & RADEON_SURF_SBUFFER) { + *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_1D; + } + if (surf->flags & RADEON_SURF_ZBUFFER) { + *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_1D; + } else if (surf->flags & RADEON_SURF_SCANOUT) { + *tile_mode = SI_TILE_MODE_COLOR_1D_SCANOUT; + } else { + *tile_mode = SI_TILE_MODE_COLOR_1D; + } + break; + case RADEON_SURF_MODE_LINEAR_ALIGNED: + default: + *tile_mode = SI_TILE_MODE_COLOR_LINEAR_ALIGNED; + } + + return 0; +} + +static void si_surf_minify(struct radeon_surface *surf, + struct radeon_surface_level *surflevel, + unsigned bpe, unsigned level, + uint32_t xalign, uint32_t yalign, uint32_t zalign, + uint32_t slice_align, uint64_t offset) +{ + if (level == 0) { + surflevel->npix_x = surf->npix_x; + } else { + surflevel->npix_x = mip_minify(next_power_of_two(surf->npix_x), level); + } + surflevel->npix_y = mip_minify(surf->npix_y, level); + surflevel->npix_z = mip_minify(surf->npix_z, level); + + if (level == 0 && surf->last_level > 0) { + surflevel->nblk_x = (next_power_of_two(surflevel->npix_x) + surf->blk_w - 1) / surf->blk_w; + surflevel->nblk_y = (next_power_of_two(surflevel->npix_y) + surf->blk_h - 1) / surf->blk_h; + surflevel->nblk_z = (next_power_of_two(surflevel->npix_z) + surf->blk_d - 1) / surf->blk_d; + } else { + surflevel->nblk_x = (surflevel->npix_x + surf->blk_w - 1) / surf->blk_w; + surflevel->nblk_y = (surflevel->npix_y + surf->blk_h - 1) / surf->blk_h; + surflevel->nblk_z = (surflevel->npix_z + surf->blk_d - 1) / surf->blk_d; + } + + surflevel->nblk_y = ALIGN(surflevel->nblk_y, yalign); + + /* XXX: Texture sampling uses unexpectedly large pitches in some cases, + * these are just guesses for the rules behind those + */ + if (level == 0 && surf->last_level == 0) + /* Non-mipmap pitch padded to slice alignment */ + /* Using just bpe here breaks stencil blitting; surf->bpe works. */ + xalign = MAX2(xalign, slice_align / surf->bpe); + else if (surflevel->mode == RADEON_SURF_MODE_LINEAR_ALIGNED) + /* Small rows evenly distributed across slice */ + xalign = MAX2(xalign, slice_align / bpe / surflevel->nblk_y); + + surflevel->nblk_x = ALIGN(surflevel->nblk_x, xalign); + surflevel->nblk_z = ALIGN(surflevel->nblk_z, zalign); + + surflevel->offset = offset; + surflevel->pitch_bytes = surflevel->nblk_x * bpe * surf->nsamples; + surflevel->slice_size = ALIGN((uint64_t)surflevel->pitch_bytes * surflevel->nblk_y, + (uint64_t)slice_align); + + surf->bo_size = offset + surflevel->slice_size * surflevel->nblk_z * surf->array_size; +} + +static void si_surf_minify_2d(struct radeon_surface *surf, + struct radeon_surface_level *surflevel, + unsigned bpe, unsigned level, unsigned slice_pt, + uint32_t xalign, uint32_t yalign, uint32_t zalign, + unsigned mtileb, uint64_t offset) +{ + unsigned mtile_pr, mtile_ps; + + if (level == 0) { + surflevel->npix_x = surf->npix_x; + } else { + surflevel->npix_x = mip_minify(next_power_of_two(surf->npix_x), level); + } + surflevel->npix_y = mip_minify(surf->npix_y, level); + surflevel->npix_z = mip_minify(surf->npix_z, level); + + if (level == 0 && surf->last_level > 0) { + surflevel->nblk_x = (next_power_of_two(surflevel->npix_x) + surf->blk_w - 1) / surf->blk_w; + surflevel->nblk_y = (next_power_of_two(surflevel->npix_y) + surf->blk_h - 1) / surf->blk_h; + surflevel->nblk_z = (next_power_of_two(surflevel->npix_z) + surf->blk_d - 1) / surf->blk_d; + } else { + surflevel->nblk_x = (surflevel->npix_x + surf->blk_w - 1) / surf->blk_w; + surflevel->nblk_y = (surflevel->npix_y + surf->blk_h - 1) / surf->blk_h; + surflevel->nblk_z = (surflevel->npix_z + surf->blk_d - 1) / surf->blk_d; + } + + if (surf->nsamples == 1 && surflevel->mode == RADEON_SURF_MODE_2D && + !(surf->flags & RADEON_SURF_FMASK)) { + if (surflevel->nblk_x < xalign || surflevel->nblk_y < yalign) { + surflevel->mode = RADEON_SURF_MODE_1D; + return; + } + } + surflevel->nblk_x = ALIGN(surflevel->nblk_x, xalign); + surflevel->nblk_y = ALIGN(surflevel->nblk_y, yalign); + surflevel->nblk_z = ALIGN(surflevel->nblk_z, zalign); + + /* macro tile per row */ + mtile_pr = surflevel->nblk_x / xalign; + /* macro tile per slice */ + mtile_ps = (mtile_pr * surflevel->nblk_y) / yalign; + surflevel->offset = offset; + surflevel->pitch_bytes = surflevel->nblk_x * bpe * surf->nsamples; + surflevel->slice_size = (uint64_t)mtile_ps * mtileb * slice_pt; + + surf->bo_size = offset + surflevel->slice_size * surflevel->nblk_z * surf->array_size; +} + +static int si_surface_init_linear_aligned(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned tile_mode, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign, slice_align; + unsigned i; + + /* compute alignment */ + if (!start_level) { + surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes); + } + xalign = MAX2(8, 64 / surf->bpe); + yalign = 1; + zalign = 1; + slice_align = MAX2(64 * surf->bpe, surf_man->hw_info.group_bytes); + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED; + si_surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign, zalign, slice_align, offset); + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, surf->bo_alignment); + } + if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) { + surf->tiling_index[i] = tile_mode; + } + } + return 0; +} + +static int si_surface_init_1d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + struct radeon_surface_level *level, + unsigned bpe, unsigned tile_mode, + uint64_t offset, unsigned start_level) +{ + uint32_t xalign, yalign, zalign, slice_align; + unsigned alignment = MAX2(256, surf_man->hw_info.group_bytes); + unsigned i; + + /* compute alignment */ + xalign = 8; + yalign = 8; + zalign = 1; + slice_align = surf_man->hw_info.group_bytes; + if (surf->flags & RADEON_SURF_SCANOUT) { + xalign = MAX2((bpe == 1) ? 64 : 32, xalign); + } + + if (start_level <= 1) { + surf->bo_alignment = MAX2(surf->bo_alignment, alignment); + + if (offset) { + offset = ALIGN(offset, alignment); + } + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + level[i].mode = RADEON_SURF_MODE_1D; + si_surf_minify(surf, level+i, bpe, i, xalign, yalign, zalign, slice_align, offset); + /* level0 and first mipmap need to have alignment */ + offset = surf->bo_size; + if (i == 0) { + offset = ALIGN(offset, alignment); + } + if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) { + if (surf->level == level) { + surf->tiling_index[i] = tile_mode; + /* it's ok because stencil is done after */ + surf->stencil_tiling_index[i] = tile_mode; + } else { + surf->stencil_tiling_index[i] = tile_mode; + } + } + } + return 0; +} + +static int si_surface_init_1d_miptrees(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned tile_mode, unsigned stencil_tile_mode) +{ + int r; + + r = si_surface_init_1d(surf_man, surf, surf->level, surf->bpe, tile_mode, 0, 0); + if (r) { + return r; + } + + if (surf->flags & RADEON_SURF_SBUFFER) { + r = si_surface_init_1d(surf_man, surf, surf->stencil_level, 1, stencil_tile_mode, surf->bo_size, 0); + surf->stencil_offset = surf->stencil_level[0].offset; + } + return r; +} + +static int si_surface_init_2d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + struct radeon_surface_level *level, + unsigned bpe, unsigned tile_mode, + unsigned num_pipes, unsigned num_banks, + unsigned tile_split, + uint64_t offset, + unsigned start_level) +{ + uint64_t aligned_offset = offset; + unsigned tilew, tileh, tileb; + unsigned mtilew, mtileh, mtileb; + unsigned slice_pt; + unsigned i; + + /* compute tile values */ + tilew = 8; + tileh = 8; + tileb = tilew * tileh * bpe * surf->nsamples; + /* slices per tile */ + slice_pt = 1; + if (tileb > tile_split && tile_split) { + slice_pt = tileb / tile_split; + } + tileb = tileb / slice_pt; + + /* macro tile width & height */ + mtilew = (tilew * surf->bankw * num_pipes) * surf->mtilea; + mtileh = (tileh * surf->bankh * num_banks) / surf->mtilea; + + /* macro tile bytes */ + mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb; + + if (start_level <= 1) { + unsigned alignment = MAX2(256, mtileb); + surf->bo_alignment = MAX2(surf->bo_alignment, alignment); + + if (aligned_offset) { + aligned_offset = ALIGN(aligned_offset, alignment); + } + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + level[i].mode = RADEON_SURF_MODE_2D; + si_surf_minify_2d(surf, level+i, bpe, i, slice_pt, mtilew, mtileh, 1, mtileb, aligned_offset); + if (level[i].mode == RADEON_SURF_MODE_1D) { + switch (tile_mode) { + case SI_TILE_MODE_COLOR_2D_8BPP: + case SI_TILE_MODE_COLOR_2D_16BPP: + case SI_TILE_MODE_COLOR_2D_32BPP: + case SI_TILE_MODE_COLOR_2D_64BPP: + tile_mode = SI_TILE_MODE_COLOR_1D; + break; + case SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP: + case SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP: + tile_mode = SI_TILE_MODE_COLOR_1D_SCANOUT; + break; + case SI_TILE_MODE_DEPTH_STENCIL_2D: + tile_mode = SI_TILE_MODE_DEPTH_STENCIL_1D; + break; + default: + return -EINVAL; + } + return si_surface_init_1d(surf_man, surf, level, bpe, tile_mode, offset, i); + } + /* level0 and first mipmap need to have alignment */ + aligned_offset = offset = surf->bo_size; + if (i == 0) { + aligned_offset = ALIGN(aligned_offset, surf->bo_alignment); + } + if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) { + if (surf->level == level) { + surf->tiling_index[i] = tile_mode; + /* it's ok because stencil is done after */ + surf->stencil_tiling_index[i] = tile_mode; + } else { + surf->stencil_tiling_index[i] = tile_mode; + } + } + } + return 0; +} + +static int si_surface_init_2d_miptrees(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned tile_mode, unsigned stencil_tile_mode) +{ + unsigned num_pipes, num_banks; + uint32_t gb_tile_mode; + int r; + + /* retrieve tiling mode value */ + gb_tile_mode = surf_man->hw_info.tile_mode_array[tile_mode]; + si_gb_tile_mode(gb_tile_mode, &num_pipes, &num_banks, NULL, NULL, NULL, NULL); + + r = si_surface_init_2d(surf_man, surf, surf->level, surf->bpe, tile_mode, num_pipes, num_banks, surf->tile_split, 0, 0); + if (r) { + return r; + } + + if (surf->flags & RADEON_SURF_SBUFFER) { + r = si_surface_init_2d(surf_man, surf, surf->stencil_level, 1, stencil_tile_mode, num_pipes, num_banks, surf->stencil_tile_split, surf->bo_size, 0); + surf->stencil_offset = surf->stencil_level[0].offset; + } + return r; +} + +static int si_surface_init(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, tile_mode, stencil_tile_mode; + int r; + + /* MSAA surfaces support the 2D mode only. */ + if (surf->nsamples > 1) { + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE); + } + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) { + /* zbuffer only support 1D or 2D tiled surface */ + switch (mode) { + case RADEON_SURF_MODE_1D: + case RADEON_SURF_MODE_2D: + break; + default: + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE); + break; + } + } + + r = si_surface_sanity(surf_man, surf, mode, &tile_mode, &stencil_tile_mode); + if (r) { + return r; + } + + surf->stencil_offset = 0; + surf->bo_alignment = 0; + + /* check tiling mode */ + switch (mode) { + case RADEON_SURF_MODE_LINEAR: + r = r6_surface_init_linear(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_LINEAR_ALIGNED: + r = si_surface_init_linear_aligned(surf_man, surf, tile_mode, 0, 0); + break; + case RADEON_SURF_MODE_1D: + r = si_surface_init_1d_miptrees(surf_man, surf, tile_mode, stencil_tile_mode); + break; + case RADEON_SURF_MODE_2D: + r = si_surface_init_2d_miptrees(surf_man, surf, tile_mode, stencil_tile_mode); + break; + default: + return -EINVAL; + } + return r; +} + +/* + * depending on surface + */ +static int si_surface_best(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, tile_mode, stencil_tile_mode; + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER) && + !(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX)) { + /* depth/stencil force 1d tiling for old mesa */ + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE); + } + + return si_surface_sanity(surf_man, surf, mode, &tile_mode, &stencil_tile_mode); +} + + +/* =========================================================================== + * Sea Islands family + */ +#define CIK__GB_TILE_MODE__PIPE_CONFIG(x) (((x) >> 6) & 0x1f) +#define CIK__PIPE_CONFIG__ADDR_SURF_P2 0 +#define CIK__PIPE_CONFIG__ADDR_SURF_P4_8x16 4 +#define CIK__PIPE_CONFIG__ADDR_SURF_P4_16x16 5 +#define CIK__PIPE_CONFIG__ADDR_SURF_P4_16x32 6 +#define CIK__PIPE_CONFIG__ADDR_SURF_P4_32x32 7 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_16x16_8x16 8 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_16x32_8x16 9 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_32x32_8x16 10 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_16x32_16x16 11 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x16 12 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x32 13 +#define CIK__PIPE_CONFIG__ADDR_SURF_P8_32x64_32x32 14 +#define CIK__PIPE_CONFIG__ADDR_SURF_P16_32X32_8X16 16 +#define CIK__PIPE_CONFIG__ADDR_SURF_P16_32X32_16X16 17 +#define CIK__GB_TILE_MODE__TILE_SPLIT(x) (((x) >> 11) & 0x7) +#define CIK__TILE_SPLIT__64B 0 +#define CIK__TILE_SPLIT__128B 1 +#define CIK__TILE_SPLIT__256B 2 +#define CIK__TILE_SPLIT__512B 3 +#define CIK__TILE_SPLIT__1024B 4 +#define CIK__TILE_SPLIT__2048B 5 +#define CIK__TILE_SPLIT__4096B 6 +#define CIK__GB_TILE_MODE__SAMPLE_SPLIT(x) (((x) >> 25) & 0x3) +#define CIK__SAMPLE_SPLIT__1 0 +#define CIK__SAMPLE_SPLIT__2 1 +#define CIK__SAMPLE_SPLIT__4 2 +#define CIK__SAMPLE_SPLIT__8 3 +#define CIK__GB_MACROTILE_MODE__BANK_WIDTH(x) ((x) & 0x3) +#define CIK__BANK_WIDTH__1 0 +#define CIK__BANK_WIDTH__2 1 +#define CIK__BANK_WIDTH__4 2 +#define CIK__BANK_WIDTH__8 3 +#define CIK__GB_MACROTILE_MODE__BANK_HEIGHT(x) (((x) >> 2) & 0x3) +#define CIK__BANK_HEIGHT__1 0 +#define CIK__BANK_HEIGHT__2 1 +#define CIK__BANK_HEIGHT__4 2 +#define CIK__BANK_HEIGHT__8 3 +#define CIK__GB_MACROTILE_MODE__MACRO_TILE_ASPECT(x) (((x) >> 4) & 0x3) +#define CIK__MACRO_TILE_ASPECT__1 0 +#define CIK__MACRO_TILE_ASPECT__2 1 +#define CIK__MACRO_TILE_ASPECT__4 2 +#define CIK__MACRO_TILE_ASPECT__8 3 +#define CIK__GB_MACROTILE_MODE__NUM_BANKS(x) (((x) >> 6) & 0x3) +#define CIK__NUM_BANKS__2_BANK 0 +#define CIK__NUM_BANKS__4_BANK 1 +#define CIK__NUM_BANKS__8_BANK 2 +#define CIK__NUM_BANKS__16_BANK 3 + + +static void cik_get_2d_params(struct radeon_surface_manager *surf_man, + unsigned bpe, unsigned nsamples, bool is_color, + unsigned tile_mode, + uint32_t *num_pipes, + uint32_t *tile_split_ptr, + uint32_t *num_banks, + uint32_t *macro_tile_aspect, + uint32_t *bank_w, + uint32_t *bank_h) +{ + uint32_t gb_tile_mode = surf_man->hw_info.tile_mode_array[tile_mode]; + unsigned tileb_1x, tileb; + unsigned gb_macrotile_mode; + unsigned macrotile_index; + unsigned tile_split, sample_split; + + if (num_pipes) { + switch (CIK__GB_TILE_MODE__PIPE_CONFIG(gb_tile_mode)) { + case CIK__PIPE_CONFIG__ADDR_SURF_P2: + default: + *num_pipes = 2; + break; + case CIK__PIPE_CONFIG__ADDR_SURF_P4_8x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P4_16x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P4_16x32: + case CIK__PIPE_CONFIG__ADDR_SURF_P4_32x32: + *num_pipes = 4; + break; + case CIK__PIPE_CONFIG__ADDR_SURF_P8_16x16_8x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P8_16x32_8x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P8_32x32_8x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P8_16x32_16x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x16: + case CIK__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x32: + case CIK__PIPE_CONFIG__ADDR_SURF_P8_32x64_32x32: + *num_pipes = 8; + break; + case CIK__PIPE_CONFIG__ADDR_SURF_P16_32X32_8X16: + case CIK__PIPE_CONFIG__ADDR_SURF_P16_32X32_16X16: + *num_pipes = 16; + break; + } + } + switch (CIK__GB_TILE_MODE__TILE_SPLIT(gb_tile_mode)) { + default: + case CIK__TILE_SPLIT__64B: + tile_split = 64; + break; + case CIK__TILE_SPLIT__128B: + tile_split = 128; + break; + case CIK__TILE_SPLIT__256B: + tile_split = 256; + break; + case CIK__TILE_SPLIT__512B: + tile_split = 512; + break; + case CIK__TILE_SPLIT__1024B: + tile_split = 1024; + break; + case CIK__TILE_SPLIT__2048B: + tile_split = 2048; + break; + case CIK__TILE_SPLIT__4096B: + tile_split = 4096; + break; + } + switch (CIK__GB_TILE_MODE__SAMPLE_SPLIT(gb_tile_mode)) { + default: + case CIK__SAMPLE_SPLIT__1: + sample_split = 1; + break; + case CIK__SAMPLE_SPLIT__2: + sample_split = 2; + break; + case CIK__SAMPLE_SPLIT__4: + sample_split = 4; + break; + case CIK__SAMPLE_SPLIT__8: + sample_split = 8; + break; + } + + /* Adjust the tile split. */ + tileb_1x = 8 * 8 * bpe; + if (is_color) { + tile_split = MAX2(256, sample_split * tileb_1x); + } + tile_split = MIN2(surf_man->hw_info.row_size, tile_split); + + /* Determine the macrotile index. */ + tileb = MIN2(tile_split, nsamples * tileb_1x); + + for (macrotile_index = 0; tileb > 64; macrotile_index++) { + tileb >>= 1; + } + gb_macrotile_mode = surf_man->hw_info.macrotile_mode_array[macrotile_index]; + + if (tile_split_ptr) { + *tile_split_ptr = tile_split; + } + if (num_banks) { + switch (CIK__GB_MACROTILE_MODE__NUM_BANKS(gb_macrotile_mode)) { + default: + case CIK__NUM_BANKS__2_BANK: + *num_banks = 2; + break; + case CIK__NUM_BANKS__4_BANK: + *num_banks = 4; + break; + case CIK__NUM_BANKS__8_BANK: + *num_banks = 8; + break; + case CIK__NUM_BANKS__16_BANK: + *num_banks = 16; + break; + } + } + if (macro_tile_aspect) { + switch (CIK__GB_MACROTILE_MODE__MACRO_TILE_ASPECT(gb_macrotile_mode)) { + default: + case CIK__MACRO_TILE_ASPECT__1: + *macro_tile_aspect = 1; + break; + case CIK__MACRO_TILE_ASPECT__2: + *macro_tile_aspect = 2; + break; + case CIK__MACRO_TILE_ASPECT__4: + *macro_tile_aspect = 4; + break; + case CIK__MACRO_TILE_ASPECT__8: + *macro_tile_aspect = 8; + break; + } + } + if (bank_w) { + switch (CIK__GB_MACROTILE_MODE__BANK_WIDTH(gb_macrotile_mode)) { + default: + case CIK__BANK_WIDTH__1: + *bank_w = 1; + break; + case CIK__BANK_WIDTH__2: + *bank_w = 2; + break; + case CIK__BANK_WIDTH__4: + *bank_w = 4; + break; + case CIK__BANK_WIDTH__8: + *bank_w = 8; + break; + } + } + if (bank_h) { + switch (CIK__GB_MACROTILE_MODE__BANK_HEIGHT(gb_macrotile_mode)) { + default: + case CIK__BANK_HEIGHT__1: + *bank_h = 1; + break; + case CIK__BANK_HEIGHT__2: + *bank_h = 2; + break; + case CIK__BANK_HEIGHT__4: + *bank_h = 4; + break; + case CIK__BANK_HEIGHT__8: + *bank_h = 8; + break; + } + } +} + +static int cik_init_hw_info(struct radeon_surface_manager *surf_man) +{ + uint32_t tiling_config; + drmVersionPtr version; + int r; + + r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG, + &tiling_config); + if (r) { + return r; + } + + surf_man->hw_info.allow_2d = 0; + version = drmGetVersion(surf_man->fd); + if (version && version->version_minor >= 35) { + if (!radeon_get_value(surf_man->fd, RADEON_INFO_SI_TILE_MODE_ARRAY, surf_man->hw_info.tile_mode_array) && + !radeon_get_value(surf_man->fd, RADEON_INFO_CIK_MACROTILE_MODE_ARRAY, surf_man->hw_info.macrotile_mode_array)) { + surf_man->hw_info.allow_2d = 1; + } + } + drmFreeVersion(version); + + switch (tiling_config & 0xf) { + case 0: + surf_man->hw_info.num_pipes = 1; + break; + case 1: + surf_man->hw_info.num_pipes = 2; + break; + case 2: + surf_man->hw_info.num_pipes = 4; + break; + case 3: + surf_man->hw_info.num_pipes = 8; + break; + default: + surf_man->hw_info.num_pipes = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf0) >> 4) { + case 0: + surf_man->hw_info.num_banks = 4; + break; + case 1: + surf_man->hw_info.num_banks = 8; + break; + case 2: + surf_man->hw_info.num_banks = 16; + break; + default: + surf_man->hw_info.num_banks = 8; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf00) >> 8) { + case 0: + surf_man->hw_info.group_bytes = 256; + break; + case 1: + surf_man->hw_info.group_bytes = 512; + break; + default: + surf_man->hw_info.group_bytes = 256; + surf_man->hw_info.allow_2d = 0; + break; + } + + switch ((tiling_config & 0xf000) >> 12) { + case 0: + surf_man->hw_info.row_size = 1024; + break; + case 1: + surf_man->hw_info.row_size = 2048; + break; + case 2: + surf_man->hw_info.row_size = 4096; + break; + default: + surf_man->hw_info.row_size = 4096; + surf_man->hw_info.allow_2d = 0; + break; + } + return 0; +} + +static int cik_surface_sanity(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned mode, unsigned *tile_mode, unsigned *stencil_tile_mode) +{ + /* check surface dimension */ + if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) { + return -EINVAL; + } + + /* check mipmap last_level */ + if (surf->last_level > 15) { + return -EINVAL; + } + + /* force 1d on kernel that can't do 2d */ + if (mode > RADEON_SURF_MODE_1D && + (!surf_man->hw_info.allow_2d || !(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX))) { + if (surf->nsamples > 1) { + fprintf(stderr, "radeon: Cannot use 1D tiling for an MSAA surface (%i).\n", __LINE__); + return -EFAULT; + } + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(mode, MODE); + } + + if (surf->nsamples > 1 && mode != RADEON_SURF_MODE_2D) { + return -EINVAL; + } + + if (!surf->tile_split) { + /* default value */ + surf->mtilea = 1; + surf->bankw = 1; + surf->bankh = 1; + surf->tile_split = 64; + surf->stencil_tile_split = 64; + } + + switch (mode) { + case RADEON_SURF_MODE_2D: { + if (surf->flags & RADEON_SURF_Z_OR_SBUFFER) { + switch (surf->nsamples) { + case 1: + *tile_mode = CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_64; + break; + case 2: + case 4: + *tile_mode = CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_128; + break; + case 8: + *tile_mode = CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_256; + break; + default: + return -EINVAL; + } + + if (surf->flags & RADEON_SURF_SBUFFER) { + *stencil_tile_mode = *tile_mode; + + cik_get_2d_params(surf_man, 1, surf->nsamples, false, + *stencil_tile_mode, NULL, + &surf->stencil_tile_split, + NULL, NULL, NULL, NULL); + } + } else if (surf->flags & RADEON_SURF_SCANOUT) { + *tile_mode = CIK_TILE_MODE_COLOR_2D_SCANOUT; + } else { + *tile_mode = CIK_TILE_MODE_COLOR_2D; + } + + /* retrieve tiling mode values */ + cik_get_2d_params(surf_man, surf->bpe, surf->nsamples, + !(surf->flags & RADEON_SURF_Z_OR_SBUFFER), *tile_mode, + NULL, &surf->tile_split, NULL, &surf->mtilea, + &surf->bankw, &surf->bankh); + break; + } + case RADEON_SURF_MODE_1D: + if (surf->flags & RADEON_SURF_SBUFFER) { + *stencil_tile_mode = CIK_TILE_MODE_DEPTH_STENCIL_1D; + } + if (surf->flags & RADEON_SURF_ZBUFFER) { + *tile_mode = CIK_TILE_MODE_DEPTH_STENCIL_1D; + } else if (surf->flags & RADEON_SURF_SCANOUT) { + *tile_mode = SI_TILE_MODE_COLOR_1D_SCANOUT; + } else { + *tile_mode = SI_TILE_MODE_COLOR_1D; + } + break; + case RADEON_SURF_MODE_LINEAR_ALIGNED: + default: + *stencil_tile_mode = SI_TILE_MODE_COLOR_LINEAR_ALIGNED; + *tile_mode = SI_TILE_MODE_COLOR_LINEAR_ALIGNED; + } + + return 0; +} + +static int cik_surface_init_2d(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + struct radeon_surface_level *level, + unsigned bpe, unsigned tile_mode, + unsigned tile_split, + unsigned num_pipes, unsigned num_banks, + uint64_t offset, + unsigned start_level) +{ + uint64_t aligned_offset = offset; + unsigned tilew, tileh, tileb_1x, tileb; + unsigned mtilew, mtileh, mtileb; + unsigned slice_pt; + unsigned i; + + /* compute tile values */ + tilew = 8; + tileh = 8; + tileb_1x = tilew * tileh * bpe; + + tile_split = MIN2(surf_man->hw_info.row_size, tile_split); + + tileb = surf->nsamples * tileb_1x; + + /* slices per tile */ + slice_pt = 1; + if (tileb > tile_split && tile_split) { + slice_pt = tileb / tile_split; + tileb = tileb / slice_pt; + } + + /* macro tile width & height */ + mtilew = (tilew * surf->bankw * num_pipes) * surf->mtilea; + mtileh = (tileh * surf->bankh * num_banks) / surf->mtilea; + + /* macro tile bytes */ + mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb; + + if (start_level <= 1) { + unsigned alignment = MAX2(256, mtileb); + surf->bo_alignment = MAX2(surf->bo_alignment, alignment); + + if (aligned_offset) { + aligned_offset = ALIGN(aligned_offset, alignment); + } + } + + /* build mipmap tree */ + for (i = start_level; i <= surf->last_level; i++) { + level[i].mode = RADEON_SURF_MODE_2D; + si_surf_minify_2d(surf, level+i, bpe, i, slice_pt, mtilew, mtileh, 1, mtileb, aligned_offset); + if (level[i].mode == RADEON_SURF_MODE_1D) { + switch (tile_mode) { + case CIK_TILE_MODE_COLOR_2D: + tile_mode = SI_TILE_MODE_COLOR_1D; + break; + case CIK_TILE_MODE_COLOR_2D_SCANOUT: + tile_mode = SI_TILE_MODE_COLOR_1D_SCANOUT; + break; + case CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_64: + case CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_128: + case CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_256: + case CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_512: + case CIK_TILE_MODE_DEPTH_STENCIL_2D_TILESPLIT_ROW_SIZE: + tile_mode = CIK_TILE_MODE_DEPTH_STENCIL_1D; + break; + default: + return -EINVAL; + } + return si_surface_init_1d(surf_man, surf, level, bpe, tile_mode, offset, i); + } + /* level0 and first mipmap need to have alignment */ + aligned_offset = offset = surf->bo_size; + if (i == 0) { + aligned_offset = ALIGN(aligned_offset, surf->bo_alignment); + } + if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) { + if (surf->level == level) { + surf->tiling_index[i] = tile_mode; + /* it's ok because stencil is done after */ + surf->stencil_tiling_index[i] = tile_mode; + } else { + surf->stencil_tiling_index[i] = tile_mode; + } + } + } + return 0; +} + +static int cik_surface_init_2d_miptrees(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned tile_mode, unsigned stencil_tile_mode) +{ + int r; + uint32_t num_pipes, num_banks; + + cik_get_2d_params(surf_man, surf->bpe, surf->nsamples, + !(surf->flags & RADEON_SURF_Z_OR_SBUFFER), tile_mode, + &num_pipes, NULL, &num_banks, NULL, NULL, NULL); + + r = cik_surface_init_2d(surf_man, surf, surf->level, surf->bpe, tile_mode, + surf->tile_split, num_pipes, num_banks, 0, 0); + if (r) { + return r; + } + + if (surf->flags & RADEON_SURF_SBUFFER) { + r = cik_surface_init_2d(surf_man, surf, surf->stencil_level, 1, stencil_tile_mode, + surf->stencil_tile_split, num_pipes, num_banks, + surf->bo_size, 0); + surf->stencil_offset = surf->stencil_level[0].offset; + } + return r; +} + +static int cik_surface_init(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, tile_mode, stencil_tile_mode; + int r; + + /* MSAA surfaces support the 2D mode only. */ + if (surf->nsamples > 1) { + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE); + } + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) { + /* zbuffer only support 1D or 2D tiled surface */ + switch (mode) { + case RADEON_SURF_MODE_1D: + case RADEON_SURF_MODE_2D: + break; + default: + mode = RADEON_SURF_MODE_1D; + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE); + break; + } + } + + r = cik_surface_sanity(surf_man, surf, mode, &tile_mode, &stencil_tile_mode); + if (r) { + return r; + } + + surf->stencil_offset = 0; + surf->bo_alignment = 0; + + /* check tiling mode */ + switch (mode) { + case RADEON_SURF_MODE_LINEAR: + r = r6_surface_init_linear(surf_man, surf, 0, 0); + break; + case RADEON_SURF_MODE_LINEAR_ALIGNED: + r = si_surface_init_linear_aligned(surf_man, surf, tile_mode, 0, 0); + break; + case RADEON_SURF_MODE_1D: + r = si_surface_init_1d_miptrees(surf_man, surf, tile_mode, stencil_tile_mode); + break; + case RADEON_SURF_MODE_2D: + r = cik_surface_init_2d_miptrees(surf_man, surf, tile_mode, stencil_tile_mode); + break; + default: + return -EINVAL; + } + return r; +} + +/* + * depending on surface + */ +static int cik_surface_best(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, tile_mode, stencil_tile_mode; + + /* tiling mode */ + mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK; + + if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER) && + !(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX)) { + /* depth/stencil force 1d tiling for old mesa */ + surf->flags = RADEON_SURF_CLR(surf->flags, MODE); + surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE); + } + + return cik_surface_sanity(surf_man, surf, mode, &tile_mode, &stencil_tile_mode); +} + + +/* =========================================================================== + * public API + */ +drm_public struct radeon_surface_manager * +radeon_surface_manager_new(int fd) +{ + struct radeon_surface_manager *surf_man; + + surf_man = calloc(1, sizeof(struct radeon_surface_manager)); + if (surf_man == NULL) { + return NULL; + } + surf_man->fd = fd; + if (radeon_get_value(fd, RADEON_INFO_DEVICE_ID, &surf_man->device_id)) { + goto out_err; + } + if (radeon_get_family(surf_man)) { + goto out_err; + } + + if (surf_man->family <= CHIP_RV740) { + if (r6_init_hw_info(surf_man)) { + goto out_err; + } + surf_man->surface_init = &r6_surface_init; + surf_man->surface_best = &r6_surface_best; + } else if (surf_man->family <= CHIP_ARUBA) { + if (eg_init_hw_info(surf_man)) { + goto out_err; + } + surf_man->surface_init = &eg_surface_init; + surf_man->surface_best = &eg_surface_best; + } else if (surf_man->family < CHIP_BONAIRE) { + if (si_init_hw_info(surf_man)) { + goto out_err; + } + surf_man->surface_init = &si_surface_init; + surf_man->surface_best = &si_surface_best; + } else { + if (cik_init_hw_info(surf_man)) { + goto out_err; + } + surf_man->surface_init = &cik_surface_init; + surf_man->surface_best = &cik_surface_best; + } + + return surf_man; +out_err: + free(surf_man); + return NULL; +} + +drm_public void +radeon_surface_manager_free(struct radeon_surface_manager *surf_man) +{ + free(surf_man); +} + +static int radeon_surface_sanity(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf, + unsigned type, + unsigned mode) +{ + if (surf_man == NULL || surf_man->surface_init == NULL || surf == NULL) { + return -EINVAL; + } + + /* all dimension must be at least 1 ! */ + if (!surf->npix_x || !surf->npix_y || !surf->npix_z) { + return -EINVAL; + } + if (!surf->blk_w || !surf->blk_h || !surf->blk_d) { + return -EINVAL; + } + if (!surf->array_size) { + return -EINVAL; + } + /* array size must be a power of 2 */ + surf->array_size = next_power_of_two(surf->array_size); + + switch (surf->nsamples) { + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + /* check type */ + switch (type) { + case RADEON_SURF_TYPE_1D: + if (surf->npix_y > 1) { + return -EINVAL; + } + /* fallthrough */ + case RADEON_SURF_TYPE_2D: + if (surf->npix_z > 1) { + return -EINVAL; + } + break; + case RADEON_SURF_TYPE_CUBEMAP: + if (surf->npix_z > 1) { + return -EINVAL; + } + /* deal with cubemap as they were texture array */ + if (surf_man->family >= CHIP_RV770) { + surf->array_size = 8; + } else { + surf->array_size = 6; + } + break; + case RADEON_SURF_TYPE_3D: + break; + case RADEON_SURF_TYPE_1D_ARRAY: + if (surf->npix_y > 1) { + return -EINVAL; + } + case RADEON_SURF_TYPE_2D_ARRAY: + break; + default: + return -EINVAL; + } + return 0; +} + +drm_public int +radeon_surface_init(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, type; + int r; + + type = RADEON_SURF_GET(surf->flags, TYPE); + mode = RADEON_SURF_GET(surf->flags, MODE); + + r = radeon_surface_sanity(surf_man, surf, type, mode); + if (r) { + return r; + } + return surf_man->surface_init(surf_man, surf); +} + +drm_public int +radeon_surface_best(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf) +{ + unsigned mode, type; + int r; + + type = RADEON_SURF_GET(surf->flags, TYPE); + mode = RADEON_SURF_GET(surf->flags, MODE); + + r = radeon_surface_sanity(surf_man, surf, type, mode); + if (r) { + return r; + } + return surf_man->surface_best(surf_man, surf); +} diff --git a/radeon/radeon_surface.h b/radeon/radeon_surface.h new file mode 100644 index 0000000..af7cab6 --- /dev/null +++ b/radeon/radeon_surface.h @@ -0,0 +1,149 @@ +/* + * Copyright © 2011 Red Hat 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. + */ +/* + * Authors: + * Jérôme Glisse <jglisse@redhat.com> + */ +#ifndef RADEON_SURFACE_H +#define RADEON_SURFACE_H + +/* Note : + * + * For texture array, the n layer are stored one after the other within each + * mipmap level. 0 value for field than can be hint is always valid. + */ + +#define RADEON_SURF_MAX_LEVEL 32 + +#define RADEON_SURF_TYPE_MASK 0xFF +#define RADEON_SURF_TYPE_SHIFT 0 +#define RADEON_SURF_TYPE_1D 0 +#define RADEON_SURF_TYPE_2D 1 +#define RADEON_SURF_TYPE_3D 2 +#define RADEON_SURF_TYPE_CUBEMAP 3 +#define RADEON_SURF_TYPE_1D_ARRAY 4 +#define RADEON_SURF_TYPE_2D_ARRAY 5 +#define RADEON_SURF_MODE_MASK 0xFF +#define RADEON_SURF_MODE_SHIFT 8 +#define RADEON_SURF_MODE_LINEAR 0 +#define RADEON_SURF_MODE_LINEAR_ALIGNED 1 +#define RADEON_SURF_MODE_1D 2 +#define RADEON_SURF_MODE_2D 3 +#define RADEON_SURF_SCANOUT (1 << 16) +#define RADEON_SURF_ZBUFFER (1 << 17) +#define RADEON_SURF_SBUFFER (1 << 18) +#define RADEON_SURF_Z_OR_SBUFFER (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER) +#define RADEON_SURF_HAS_SBUFFER_MIPTREE (1 << 19) +#define RADEON_SURF_HAS_TILE_MODE_INDEX (1 << 20) +#define RADEON_SURF_FMASK (1 << 21) + +#define RADEON_SURF_GET(v, field) (((v) >> RADEON_SURF_ ## field ## _SHIFT) & RADEON_SURF_ ## field ## _MASK) +#define RADEON_SURF_SET(v, field) (((v) & RADEON_SURF_ ## field ## _MASK) << RADEON_SURF_ ## field ## _SHIFT) +#define RADEON_SURF_CLR(v, field) ((v) & ~(RADEON_SURF_ ## field ## _MASK << RADEON_SURF_ ## field ## _SHIFT)) + +/* first field up to mode need to match r6 struct so that we can reuse + * same function for linear & linear aligned + */ +struct radeon_surface_level { + uint64_t offset; + uint64_t slice_size; + uint32_t npix_x; + uint32_t npix_y; + uint32_t npix_z; + uint32_t nblk_x; + uint32_t nblk_y; + uint32_t nblk_z; + uint32_t pitch_bytes; + uint32_t mode; +}; + +enum si_tiling_mode { + SI_TILING_AUTO = 0, + + SI_TILING_COLOR_1D, + SI_TILING_COLOR_1D_SCANOUT, + SI_TILING_COLOR_2D_8BPP, + SI_TILING_COLOR_2D_16BPP, + SI_TILING_COLOR_2D_32BPP, + SI_TILING_COLOR_2D_64BPP, + SI_TILING_COLOR_2D_SCANOUT_16BPP, + SI_TILING_COLOR_2D_SCANOUT_32BPP, + SI_TILING_COLOR_LINEAR, + + SI_TILING_STENCIL_1D, + SI_TILING_STENCIL_2D, + SI_TILING_STENCIL_2D_2AA, + SI_TILING_STENCIL_2D_4AA, + SI_TILING_STENCIL_2D_8AA, + + SI_TILING_DEPTH_1D, + SI_TILING_DEPTH_2D, + SI_TILING_DEPTH_2D_2AA, + SI_TILING_DEPTH_2D_4AA, + SI_TILING_DEPTH_2D_8AA, + + SI_TILING_LAST_MODE, +}; + +struct radeon_surface { + uint32_t npix_x; + uint32_t npix_y; + uint32_t npix_z; + uint32_t blk_w; + uint32_t blk_h; + uint32_t blk_d; + uint32_t array_size; + uint32_t last_level; + uint32_t bpe; + uint32_t nsamples; + uint32_t flags; + /* Following is updated/fill by the allocator. It's allowed to + * set some of the value but they are use as hint and can be + * overridden (things lile bankw/bankh on evergreen for + * instance). + */ + uint64_t bo_size; + uint64_t bo_alignment; + /* apply to eg */ + uint32_t bankw; + uint32_t bankh; + uint32_t mtilea; + uint32_t tile_split; + uint32_t stencil_tile_split; + uint64_t stencil_offset; + struct radeon_surface_level level[RADEON_SURF_MAX_LEVEL]; + struct radeon_surface_level stencil_level[RADEON_SURF_MAX_LEVEL]; + uint32_t tiling_index[RADEON_SURF_MAX_LEVEL]; + uint32_t stencil_tiling_index[RADEON_SURF_MAX_LEVEL]; +}; + +struct radeon_surface_manager *radeon_surface_manager_new(int fd); +void radeon_surface_manager_free(struct radeon_surface_manager *surf_man); +int radeon_surface_init(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf); +int radeon_surface_best(struct radeon_surface_manager *surf_man, + struct radeon_surface *surf); + +#endif |