1 /* 2 * Copyright 2008 Jrme Glisse 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 15 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS 17 * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * The above copyright notice and this permission notice (including the 23 * next paragraph) shall be included in all copies or substantial portions 24 * of the Software. 25 */ 26 /* 27 * Authors: 28 * Aapo Tahkola <aet (at) rasterburn.org> 29 * Nicolai Haehnle <prefect_ (at) gmx.net> 30 * Jrme Glisse <glisse (at) freedesktop.org> 31 */ 32 #include <assert.h> 33 #include <errno.h> 34 #include <stdlib.h> 35 #include <sys/mman.h> 36 #include <sys/ioctl.h> 37 #include "radeon_cs.h" 38 #include "radeon_cs_gem.h" 39 #include "radeon_bo_gem.h" 40 #include "drm.h" 41 #include "xf86drm.h" 42 #include "radeon_drm.h" 43 44 #pragma pack(1) 45 struct cs_reloc_gem { 46 uint32_t handle; 47 uint32_t read_domain; 48 uint32_t write_domain; 49 uint32_t flags; 50 }; 51 52 #pragma pack() 53 #define RELOC_SIZE (sizeof(struct cs_reloc_gem) / sizeof(uint32_t)) 54 55 struct cs_gem { 56 struct radeon_cs base; 57 struct drm_radeon_cs cs; 58 struct drm_radeon_cs_chunk chunks[2]; 59 unsigned nrelocs; 60 uint32_t *relocs; 61 struct radeon_bo **relocs_bo; 62 }; 63 64 static struct radeon_cs *cs_gem_create(struct radeon_cs_manager *csm, 65 uint32_t ndw) 66 { 67 struct cs_gem *csg; 68 69 /* max cmd buffer size is 64Kb */ 70 if (ndw > (64 * 1024 / 4)) { 71 return NULL; 72 } 73 csg = (struct cs_gem*)calloc(1, sizeof(struct cs_gem)); 74 if (csg == NULL) { 75 return NULL; 76 } 77 csg->base.csm = csm; 78 csg->base.ndw = 64 * 1024 / 4; 79 csg->base.packets = (uint32_t*)calloc(1, 64 * 1024); 80 if (csg->base.packets == NULL) { 81 free(csg); 82 return NULL; 83 } 84 csg->base.relocs_total_size = 0; 85 csg->base.crelocs = 0; 86 csg->nrelocs = 4096 / (4 * 4) ; 87 csg->relocs_bo = (struct radeon_bo**)calloc(1, 88 csg->nrelocs*sizeof(void*)); 89 if (csg->relocs_bo == NULL) { 90 free(csg->base.packets); 91 free(csg); 92 return NULL; 93 } 94 csg->base.relocs = csg->relocs = (uint32_t*)calloc(1, 4096); 95 if (csg->relocs == NULL) { 96 free(csg->relocs_bo); 97 free(csg->base.packets); 98 free(csg); 99 return NULL; 100 } 101 csg->chunks[0].chunk_id = RADEON_CHUNK_ID_IB; 102 csg->chunks[0].length_dw = 0; 103 csg->chunks[0].chunk_data = (uint64_t)(uintptr_t)csg->base.packets; 104 csg->chunks[1].chunk_id = RADEON_CHUNK_ID_RELOCS; 105 csg->chunks[1].length_dw = 0; 106 csg->chunks[1].chunk_data = (uint64_t)(uintptr_t)csg->relocs; 107 return (struct radeon_cs*)csg; 108 } 109 110 static int cs_gem_write_reloc(struct radeon_cs *cs, 111 struct radeon_bo *bo, 112 uint32_t read_domain, 113 uint32_t write_domain, 114 uint32_t flags) 115 { 116 struct cs_gem *csg = (struct cs_gem*)cs; 117 struct cs_reloc_gem *reloc; 118 uint32_t idx; 119 unsigned i; 120 121 assert(bo->space_accounted); 122 123 /* check domains */ 124 if ((read_domain && write_domain) || (!read_domain && !write_domain)) { 125 /* in one CS a bo can only be in read or write domain but not 126 * in read & write domain at the same sime 127 */ 128 return -EINVAL; 129 } 130 if (read_domain == RADEON_GEM_DOMAIN_CPU) { 131 return -EINVAL; 132 } 133 if (write_domain == RADEON_GEM_DOMAIN_CPU) { 134 return -EINVAL; 135 } 136 /* check if bo is already referenced */ 137 for(i = 0; i < cs->crelocs; i++) { 138 idx = i * RELOC_SIZE; 139 reloc = (struct cs_reloc_gem*)&csg->relocs[idx]; 140 if (reloc->handle == bo->handle) { 141 /* Check domains must be in read or write. As we check already 142 * checked that in argument one of the read or write domain was 143 * set we only need to check that if previous reloc as the read 144 * domain set then the read_domain should also be set for this 145 * new relocation. 146 */ 147 /* the DDX expects to read and write from same pixmap */ 148 if (write_domain && (reloc->read_domain & write_domain)) { 149 reloc->read_domain = 0; 150 reloc->write_domain = write_domain; 151 } else if (read_domain & reloc->write_domain) { 152 reloc->read_domain = 0; 153 } else { 154 if (write_domain != reloc->write_domain) 155 return -EINVAL; 156 if (read_domain != reloc->read_domain) 157 return -EINVAL; 158 } 159 160 reloc->read_domain |= read_domain; 161 reloc->write_domain |= write_domain; 162 /* update flags */ 163 reloc->flags |= (flags & reloc->flags); 164 /* write relocation packet */ 165 radeon_cs_write_dword(cs, 0xc0001000); 166 radeon_cs_write_dword(cs, idx); 167 return 0; 168 } 169 } 170 /* new relocation */ 171 if (csg->base.crelocs >= csg->nrelocs) { 172 /* allocate more memory (TODO: should use a slab allocatore maybe) */ 173 uint32_t *tmp, size; 174 size = ((csg->nrelocs + 1) * sizeof(struct radeon_bo*)); 175 tmp = (uint32_t*)realloc(csg->relocs_bo, size); 176 if (tmp == NULL) { 177 return -ENOMEM; 178 } 179 csg->relocs_bo = (struct radeon_bo**)tmp; 180 size = ((csg->nrelocs + 1) * RELOC_SIZE * 4); 181 tmp = (uint32_t*)realloc(csg->relocs, size); 182 if (tmp == NULL) { 183 return -ENOMEM; 184 } 185 cs->relocs = csg->relocs = tmp; 186 csg->nrelocs += 1; 187 csg->chunks[1].chunk_data = (uint64_t)(uintptr_t)csg->relocs; 188 } 189 csg->relocs_bo[csg->base.crelocs] = bo; 190 idx = (csg->base.crelocs++) * RELOC_SIZE; 191 reloc = (struct cs_reloc_gem*)&csg->relocs[idx]; 192 reloc->handle = bo->handle; 193 reloc->read_domain = read_domain; 194 reloc->write_domain = write_domain; 195 reloc->flags = flags; 196 csg->chunks[1].length_dw += RELOC_SIZE; 197 radeon_bo_ref(bo); 198 cs->relocs_total_size += bo->size; 199 radeon_cs_write_dword(cs, 0xc0001000); 200 radeon_cs_write_dword(cs, idx); 201 return 0; 202 } 203 204 static int cs_gem_begin(struct radeon_cs *cs, 205 uint32_t ndw, 206 const char *file, 207 const char *func, 208 int line) 209 { 210 211 if (cs->section) { 212 fprintf(stderr, "CS already in a section(%s,%s,%d)\n", 213 cs->section_file, cs->section_func, cs->section_line); 214 fprintf(stderr, "CS can't start section(%s,%s,%d)\n", 215 file, func, line); 216 return -EPIPE; 217 } 218 cs->section = 1; 219 cs->section_ndw = ndw; 220 cs->section_cdw = 0; 221 cs->section_file = file; 222 cs->section_func = func; 223 cs->section_line = line; 224 225 226 if (cs->cdw + ndw > cs->ndw) { 227 uint32_t tmp, *ptr; 228 229 tmp = (cs->ndw + 1 + 0x3FF) & (~0x3FF); 230 ptr = (uint32_t*)realloc(cs->packets, 4 * tmp); 231 if (ptr == NULL) { 232 return -ENOMEM; 233 } 234 cs->packets = ptr; 235 cs->ndw = tmp; 236 } 237 238 return 0; 239 } 240 241 static int cs_gem_end(struct radeon_cs *cs, 242 const char *file, 243 const char *func, 244 int line) 245 246 { 247 if (!cs->section) { 248 fprintf(stderr, "CS no section to end at (%s,%s,%d)\n", 249 file, func, line); 250 return -EPIPE; 251 } 252 cs->section = 0; 253 if (cs->section_ndw != cs->section_cdw) { 254 fprintf(stderr, "CS section size missmatch start at (%s,%s,%d) %d vs %d\n", 255 cs->section_file, cs->section_func, cs->section_line, cs->section_ndw, cs->section_cdw); 256 fprintf(stderr, "CS section end at (%s,%s,%d)\n", 257 file, func, line); 258 return -EPIPE; 259 } 260 return 0; 261 } 262 263 static int cs_gem_emit(struct radeon_cs *cs) 264 { 265 struct cs_gem *csg = (struct cs_gem*)cs; 266 uint64_t chunk_array[2]; 267 unsigned i; 268 int r; 269 270 csg->chunks[0].length_dw = cs->cdw; 271 272 chunk_array[0] = (uint64_t)(uintptr_t)&csg->chunks[0]; 273 chunk_array[1] = (uint64_t)(uintptr_t)&csg->chunks[1]; 274 275 csg->cs.num_chunks = 2; 276 csg->cs.chunks = (uint64_t)(uintptr_t)chunk_array; 277 278 r = drmCommandWriteRead(cs->csm->fd, DRM_RADEON_CS, 279 &csg->cs, sizeof(struct drm_radeon_cs)); 280 for (i = 0; i < csg->base.crelocs; i++) { 281 csg->relocs_bo[i]->space_accounted = 0; 282 radeon_bo_unref(csg->relocs_bo[i]); 283 csg->relocs_bo[i] = NULL; 284 } 285 286 cs->csm->read_used = 0; 287 cs->csm->vram_write_used = 0; 288 cs->csm->gart_write_used = 0; 289 return r; 290 } 291 292 static int cs_gem_destroy(struct radeon_cs *cs) 293 { 294 struct cs_gem *csg = (struct cs_gem*)cs; 295 296 free(csg->relocs_bo); 297 free(cs->relocs); 298 free(cs->packets); 299 free(cs); 300 return 0; 301 } 302 303 static int cs_gem_erase(struct radeon_cs *cs) 304 { 305 struct cs_gem *csg = (struct cs_gem*)cs; 306 unsigned i; 307 308 if (csg->relocs_bo) { 309 for (i = 0; i < csg->base.crelocs; i++) { 310 if (csg->relocs_bo[i]) { 311 radeon_bo_unref(csg->relocs_bo[i]); 312 csg->relocs_bo[i] = NULL; 313 } 314 } 315 } 316 cs->relocs_total_size = 0; 317 cs->cdw = 0; 318 cs->section = 0; 319 cs->crelocs = 0; 320 csg->chunks[0].length_dw = 0; 321 csg->chunks[1].length_dw = 0; 322 return 0; 323 } 324 325 static int cs_gem_need_flush(struct radeon_cs *cs) 326 { 327 return 0; //(cs->relocs_total_size > (32*1024*1024)); 328 } 329 330 #define PACKET_TYPE0 0 331 #define PACKET_TYPE1 1 332 #define PACKET_TYPE2 2 333 #define PACKET_TYPE3 3 334 335 #define PACKET3_NOP 0x10 336 #define PACKET3_SET_SCISSORS 0x1E 337 #define PACKET3_3D_DRAW_VBUF 0x28 338 #define PACKET3_3D_DRAW_IMMD 0x29 339 #define PACKET3_3D_DRAW_INDX 0x2A 340 #define PACKET3_3D_LOAD_VBPNTR 0x2F 341 #define PACKET3_INDX_BUFFER 0x33 342 #define PACKET3_3D_DRAW_VBUF_2 0x34 343 #define PACKET3_3D_DRAW_IMMD_2 0x35 344 #define PACKET3_3D_DRAW_INDX_2 0x36 345 346 #define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) 347 #define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) 348 #define CP_PACKET0_GET_REG(h) (((h) & 0x1FFF) << 2) 349 #define CP_PACKET0_GET_ONE_REG_WR(h) (((h) >> 15) & 1) 350 #define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) 351 352 static void cs_gem_print(struct radeon_cs *cs, FILE *file) 353 { 354 unsigned opcode; 355 unsigned reg; 356 unsigned cnt; 357 unsigned int i, j; 358 359 for (i = 0; i < cs->cdw;) { 360 cnt = CP_PACKET_GET_COUNT(cs->packets[i]) + 1; 361 switch (CP_PACKET_GET_TYPE(cs->packets[i])) { 362 case PACKET_TYPE0: 363 fprintf(file, "Pkt0 at %d (%d dwords):\n", i, cnt); 364 reg = CP_PACKET0_GET_REG(cs->packets[i]); 365 if (CP_PACKET0_GET_ONE_REG_WR(cs->packets[i++])) { 366 for (j = 0; j < cnt; j++) { 367 fprintf(file, " 0x%08X -> 0x%04X\n", 368 cs->packets[i++], reg); 369 } 370 } else { 371 for (j = 0; j < cnt; j++) { 372 fprintf(file, " 0x%08X -> 0x%04X\n", 373 cs->packets[i++], reg); 374 reg += 4; 375 } 376 } 377 break; 378 case PACKET_TYPE3: 379 fprintf(file, "Pkt3 at %d :\n", i); 380 opcode = CP_PACKET3_GET_OPCODE(cs->packets[i++]); 381 switch (opcode) { 382 case PACKET3_NOP: 383 fprintf(file, " PACKET3_NOP:\n"); 384 break; 385 case PACKET3_3D_DRAW_VBUF: 386 fprintf(file, " PACKET3_3D_DRAW_VBUF:\n"); 387 break; 388 case PACKET3_3D_DRAW_IMMD: 389 fprintf(file, " PACKET3_3D_DRAW_IMMD:\n"); 390 break; 391 case PACKET3_3D_DRAW_INDX: 392 fprintf(file, " PACKET3_3D_DRAW_INDX:\n"); 393 break; 394 case PACKET3_3D_LOAD_VBPNTR: 395 fprintf(file, " PACKET3_3D_LOAD_VBPNTR:\n"); 396 break; 397 case PACKET3_INDX_BUFFER: 398 fprintf(file, " PACKET3_INDX_BUFFER:\n"); 399 break; 400 case PACKET3_3D_DRAW_VBUF_2: 401 fprintf(file, " PACKET3_3D_DRAW_VBUF_2:\n"); 402 break; 403 case PACKET3_3D_DRAW_IMMD_2: 404 fprintf(file, " PACKET3_3D_DRAW_IMMD_2:\n"); 405 break; 406 case PACKET3_3D_DRAW_INDX_2: 407 fprintf(file, " PACKET3_3D_DRAW_INDX_2:\n"); 408 break; 409 default: 410 fprintf(file, "Unknow opcode 0x%02X at %d\n", opcode, i); 411 return; 412 } 413 for (j = 0; j < cnt; j++) { 414 fprintf(file, " 0x%08X\n", cs->packets[i++]); 415 } 416 break; 417 case PACKET_TYPE1: 418 case PACKET_TYPE2: 419 default: 420 fprintf(file, "Unknow packet 0x%08X at %d\n", cs->packets[i], i); 421 return; 422 } 423 } 424 } 425 426 427 428 static struct radeon_cs_funcs radeon_cs_gem_funcs = { 429 cs_gem_create, 430 cs_gem_write_reloc, 431 cs_gem_begin, 432 cs_gem_end, 433 cs_gem_emit, 434 cs_gem_destroy, 435 cs_gem_erase, 436 cs_gem_need_flush, 437 cs_gem_print, 438 }; 439 440 struct radeon_cs_manager *radeon_cs_manager_gem_ctor(int fd) 441 { 442 struct radeon_cs_manager *csm; 443 444 csm = (struct radeon_cs_manager*)calloc(1, 445 sizeof(struct radeon_cs_manager)); 446 if (csm == NULL) { 447 return NULL; 448 } 449 csm->funcs = &radeon_cs_gem_funcs; 450 csm->fd = fd; 451 return csm; 452 } 453 454 void radeon_cs_manager_gem_dtor(struct radeon_cs_manager *csm) 455 { 456 free(csm); 457 } 458