/* * Copyright 2011 Jerome Glisse * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * 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 */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include "xf86drm.h" #include "radeon_drm.h" #pragma pack(1) struct radeon_cs_reloc { uint32_t handle; uint32_t read_domain; uint32_t write_domain; uint32_t flags; }; #pragma pack() struct rbo { unsigned refcount; uint32_t handle; unsigned size; unsigned alignment; void *data; }; struct bo { unsigned size; uint32_t handle; void *data; struct rbo *rbo; }; struct cs { unsigned pm4_ndw; unsigned reloc_ndw; unsigned nbo; struct bo *bo; uint32_t device_id; uint32_t *pm4; uint32_t *reloc; }; struct rbo *rbo(int fd, unsigned handle, unsigned size, unsigned alignment, void *ptr); int rbo_map(int fd, struct rbo *bo); void rbo_unmap(int fd, struct rbo *bo); struct rbo *rbo_decref(int fd, struct rbo *bo); int copy_from_array(json_t *array, void *data) { uint32_t *ptr = data; unsigned i; json_t *value; for (i = 0; i < json_array_size(array); i++) { value = json_array_get(array, i); if (value == NULL) { return -EINVAL; } ptr[i] = strtoul(json_string_value(value), NULL, 0); } return 0; } int cs_json_load(struct cs *cs, const char *filename) { json_error_t error; json_t *json, *tmp, *bo_array, *bo; int i; memset(cs, 0, sizeof(struct cs)); json = json_load_file(filename, &error); if (json == NULL) { return -EINVAL; } /* device */ tmp = json_object_get(json, "device_id"); if (tmp == NULL) { fprintf(stderr, "no device id object\n"); goto out_err; } cs->device_id = json_integer_value(tmp); /* pm4 */ tmp = json_object_get(json, "pm4"); if (tmp == NULL) { fprintf(stderr, "no pm4 object\n"); goto out_err; } cs->pm4_ndw = json_array_size(tmp); cs->pm4 = calloc(1, cs->pm4_ndw * 4); if (copy_from_array(tmp, cs->pm4)) { fprintf(stderr, "failed reading pm4 object\n"); goto out_err; } /* reloc */ tmp = json_object_get(json, "reloc"); if (tmp == NULL) { fprintf(stderr, "no reloc object\n"); goto out_err; } cs->reloc_ndw = json_array_size(tmp); cs->reloc = calloc(1, cs->reloc_ndw * 4); if (copy_from_array(tmp, cs->reloc)) { fprintf(stderr, "failed reading pm4 object\n"); goto out_err; } /* bo */ bo_array = json_object_get(json, "bo"); if (bo_array == NULL) { fprintf(stderr, "no bo object\n"); goto out_err; } cs->nbo = json_array_size(bo_array); cs->bo = calloc(1, sizeof(struct bo) * cs->nbo); if (cs->bo == NULL) { fprintf(stderr, "%s failed to allocate bo\n", __func__); goto out_err; } for (i = 0; i < cs->nbo; i++) { bo = json_array_get(bo_array, i); if (bo == NULL) { fprintf(stderr, "no bo[%d] object\n", i); goto out_err; } tmp = json_object_get(bo, "size"); if (tmp == NULL) { fprintf(stderr, "no size object for bo[%d]\n", i); goto out_err; } cs->bo[i].size = json_integer_value(tmp); tmp = json_object_get(bo, "handle"); if (tmp == NULL) { fprintf(stderr, "no handle object for bo[%d]\n", i); goto out_err; } cs->bo[i].handle = json_integer_value(tmp); cs->bo[i].data = calloc(1, cs->bo[i].size); if (cs->bo[i].data == NULL) { fprintf(stderr, "failed to allocate bo[%d]\n", i); goto out_err; } tmp = json_object_get(bo, "data"); if (tmp == NULL) { fprintf(stderr, "no data object for bo[%d]\n", i); goto out_err; } if (copy_from_array(tmp, cs->bo[i].data)) { fprintf(stderr, "failed reading pm4 object\n"); goto out_err; } } json_decref(json); return 0; out_err: json_decref(json); for (i = 0; i < cs->nbo; i++) { free(cs->bo[i].data); } free(cs->pm4); free(cs->reloc); free(cs->bo); memset(cs, 0, sizeof(struct cs)); return -EINVAL; } struct rbo *rbo(int fd, unsigned handle, unsigned size, unsigned alignment, void *ptr) { struct rbo *bo; int r; bo = calloc(1, sizeof(*bo)); if (bo == NULL) { return NULL; } bo->refcount = 1; bo->size = size; bo->handle = handle; bo->alignment = alignment; if (handle) { struct drm_gem_open open_arg; memset(&open_arg, 0, sizeof(open_arg)); open_arg.name = handle; r = drmIoctl(fd, DRM_IOCTL_GEM_OPEN, &open_arg); if (r != 0) { free(bo); return NULL; } bo->handle = open_arg.handle; } else { struct drm_radeon_gem_create args; args.size = size; args.alignment = alignment; args.initial_domain = RADEON_GEM_DOMAIN_CPU; args.flags = 0; args.handle = 0; r = drmCommandWriteRead(fd, DRM_RADEON_GEM_CREATE, &args, sizeof(args)); bo->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); free(bo); return NULL; } } if (ptr) { if (rbo_map(fd, bo)) { fprintf(stderr, "%s failed to copy data into bo\n", __func__); return rbo_decref(fd, bo); } memcpy(bo->data, ptr, size); rbo_unmap(fd, bo); } return bo; } int rbo_map(int fd, struct rbo *bo) { struct drm_radeon_gem_mmap args; void *ptr; int r; /* Zero out args to make valgrind happy */ memset(&args, 0, sizeof(args)); args.handle = bo->handle; args.offset = 0; args.size = (uint64_t)bo->size; r = drmCommandWriteRead(fd, DRM_RADEON_GEM_MMAP, &args, sizeof(args)); if (r) { fprintf(stderr, "error mapping %p 0x%08X (error = %d)\n", bo, bo->handle, r); return r; } ptr = mmap(0, args.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, args.addr_ptr); if (ptr == MAP_FAILED) { fprintf(stderr, "%s failed to map bo\n", __func__); return -errno; } bo->data = ptr; return 0; } void rbo_unmap(int fd, struct rbo *bo) { munmap(bo->data, bo->size); bo->data = NULL; } struct rbo *rbo_decref(int fd, struct rbo *bo) { struct drm_gem_close args; if (bo == NULL) return NULL; if (--bo->refcount > 0) { return NULL; } if (bo->data) { munmap(bo->data, bo->size); } memset(&args, 0, sizeof(args)); args.handle = bo->handle; drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &args); memset(bo, 0, sizeof(struct rbo)); free(bo); return NULL; } int radeon_open_fd(void) { return drmOpen("radeon", NULL); } int cs_submit(int fd, struct cs *cs) { struct drm_radeon_cs drmib; struct drm_radeon_cs_chunk chunks[2]; uint64_t chunk_array[2]; int r = 0; drmib.num_chunks = 2; drmib.chunks = (uint64_t)(uintptr_t)chunk_array; chunks[0].chunk_id = RADEON_CHUNK_ID_IB; chunks[0].length_dw = cs->pm4_ndw; chunks[0].chunk_data = (uintptr_t)cs->pm4; chunks[1].chunk_id = RADEON_CHUNK_ID_RELOCS; chunks[1].length_dw = cs->reloc_ndw; chunks[1].chunk_data = (uintptr_t)cs->reloc; chunk_array[0] = (uintptr_t)&chunks[0]; chunk_array[1] = (uintptr_t)&chunks[1]; #if 1 r = drmCommandWriteRead(fd, DRM_RADEON_CS, &drmib, sizeof(struct drm_radeon_cs)); #endif return r; } int main(int argc, char *argv[]) { unsigned i, j; struct cs cs; int radeonfd; struct radeon_cs_reloc *reloc; if (argc != 2) { fprintf(stderr, "usage : %s jsonfile\n", argv[0]); return -EINVAL; } radeonfd = radeon_open_fd(); if (radeonfd < 0) { fprintf(stderr, "failed to radeon fd\n"); return -1; } if (cs_json_load(&cs, argv[1])) { fprintf(stderr, "failed to load json\n"); return -EINVAL; } /* allocate bo */ for (i = 0; i < cs.nbo; i++) { cs.bo[i].rbo = rbo(radeonfd, 0, cs.bo[i].size, 4096, cs.bo[i].data); if (cs.bo[i].rbo == NULL) { fprintf(stderr, "failed to allocate hw bo[%d]\n", i); return -EINVAL; } if (!rbo_map(radeonfd, cs.bo[i].rbo)) { fprintf(stderr, "bo[%d][0x%08X][0] =", i, cs.bo[i].rbo->size); for (j = 0; j < 4; j++) { fprintf(stderr, " 0x%08x", ((unsigned*)cs.bo[i].rbo->data)[j]); } fprintf(stderr, "\n"); rbo_unmap(radeonfd, cs.bo[i].rbo); } } /* update reloc */ reloc = (struct radeon_cs_reloc *)cs.reloc; for (i = 0; i < (cs.reloc_ndw / 4); i++) { for (j = 0; j < cs.nbo; j++) { if (reloc[i].handle == cs.bo[j].handle) { reloc[i].handle = cs.bo[j].rbo->handle; break; } } if (j == cs.nbo) { fprintf(stderr, "failed to find bo for handle %d\n", reloc[i].handle); return -EINVAL; } } printf("ready to submit, press a key to proceed\n"); getchar(); if (cs_submit(radeonfd, &cs)) { fprintf(stderr, "cs submission failed\n"); return -EINVAL; } for (i = 0; i < cs.nbo; i++) { if (!rbo_map(radeonfd, cs.bo[i].rbo)) { fprintf(stderr, "bo[%d][0x%08X][0] =", i, cs.bo[i].rbo->size); for (j = 0; j < 4; j++) { fprintf(stderr, " 0x%08x", ((unsigned*)cs.bo[i].rbo->data)[j]); } fprintf(stderr, "\n"); rbo_unmap(radeonfd, cs.bo[i].rbo); } } return 0; }