/* basic set of prime tests between intel and nouveau */ /* test list - 1. share buffer from intel -> nouveau. 2. share buffer from nouveau -> intel 3. share intel->nouveau, map on both, write intel, read nouveau 4. share intel->nouveau, blit intel fill, readback on nouveau test 1 + map buffer, read/write, map other size. do some hw actions on the buffer some illegal operations - close prime fd try and map TODO add some nouveau rendering tests */ #include "igt.h" #include #include #include #include #include #include #include #include #include #include "intel_bufmgr.h" #include "nouveau.h" static int intel_fd = -1, nouveau_fd = -1; static drm_intel_bufmgr *bufmgr; static struct nouveau_device *ndev; static struct nouveau_client *nclient; static uint32_t devid; static struct intel_batchbuffer *batch; static struct nouveau_object *nchannel, *pcopy; static struct nouveau_bufctx *nbufctx; static struct nouveau_pushbuf *npush; static struct nouveau_bo *query_bo; static uint32_t query_counter; static volatile uint32_t *query; static uint32_t memtype_intel, tile_intel_y, tile_intel_x; #define SUBC_COPY(x) 6, (x) #define NV01_SUBCHAN_OBJECT 0 #define NV01_SUBC(subc, mthd) SUBC_##subc((NV01_SUBCHAN_##mthd)) typedef struct { uint32_t w, h; uint32_t pitch, lines; } rect; static void nv_bo_alloc(struct nouveau_bo **bo, rect *r, uint32_t w, uint32_t h, uint32_t tile_mode, int handle, uint32_t dom) { uint32_t size; uint32_t dx = 1, dy = 1, memtype = 0; *bo = NULL; if (tile_mode) { uint32_t tile_y; uint32_t tile_x; /* Y major tiling */ if ((tile_mode & 0xf) == 0xe) /* but the internal layout is different */ tile_x = 7; else tile_x = 6 + (tile_mode & 0xf); if (ndev->chipset < 0xc0) { memtype = 0x70; tile_y = 2; } else { memtype = 0xfe; tile_y = 3; } if ((tile_mode & 0xf) == 0xe) memtype = memtype_intel; tile_y += ((tile_mode & 0xf0)>>4); dx = 1 << tile_x; dy = 1 << tile_y; igt_debug("Tiling requirements: x y %u %u\n", dx, dy); } r->w = w; r->h = h; r->pitch = w = (w + dx-1) & ~(dx-1); r->lines = h = (h + dy-1) & ~(dy-1); size = w*h; if (handle < 0) { union nouveau_bo_config cfg; cfg.nv50.memtype = memtype; cfg.nv50.tile_mode = tile_mode; if (dom == NOUVEAU_BO_GART) dom |= NOUVEAU_BO_MAP; igt_assert(nouveau_bo_new(ndev, dom, 4096, size, &cfg, bo) == 0); igt_assert(nouveau_bo_map(*bo, NOUVEAU_BO_RDWR, nclient) == 0); igt_debug("new flags %08x memtype %08x tile %08x\n", (*bo)->flags, (*bo)->config.nv50.memtype, (*bo)->config.nv50.tile_mode); if (tile_mode == tile_intel_y || tile_mode == tile_intel_x) { igt_debug("tile mode was: %02x, now: %02x\n", (*bo)->config.nv50.tile_mode, tile_mode); /* Doesn't like intel tiling much.. */ (*bo)->config.nv50.tile_mode = tile_mode; } } else { igt_assert(nouveau_bo_prime_handle_ref(ndev, handle, bo) == 0); close(handle); igt_assert_f((*bo)->size >= size, "expected bo size to be at least %u," "but received %"PRIu64"\n", size, (*bo)->size); igt_debug("prime flags %08x memtype %08x tile %08x\n", (*bo)->flags, (*bo)->config.nv50.memtype, (*bo)->config.nv50.tile_mode); (*bo)->config.nv50.memtype = memtype; (*bo)->config.nv50.tile_mode = tile_mode; } igt_debug("size: %"PRIu64"\n", (*bo)->size); } static inline void PUSH_DATA(struct nouveau_pushbuf *push, uint32_t data) { *push->cur++ = data; } static inline void BEGIN_NV04(struct nouveau_pushbuf *push, int subc, int mthd, int size) { PUSH_DATA (push, 0x00000000 | (size << 18) | (subc << 13) | mthd); } static inline void BEGIN_NI04(struct nouveau_pushbuf *push, int subc, int mthd, int size) { PUSH_DATA (push, 0x40000000 | (size << 18) | (subc << 13) | mthd); } static inline void BEGIN_NVC0(struct nouveau_pushbuf *push, int subc, int mthd, int size) { PUSH_DATA (push, 0x20000000 | (size << 16) | (subc << 13) | (mthd / 4)); } static inline void BEGIN_NVXX(struct nouveau_pushbuf *push, int subc, int mthd, int size) { if (ndev->chipset < 0xc0) BEGIN_NV04(push, subc, mthd, size); else BEGIN_NVC0(push, subc, mthd, size); } static void noop_intel(drm_intel_bo *bo) { BEGIN_BATCH(3, 1); OUT_BATCH(MI_NOOP); OUT_BATCH(MI_BATCH_BUFFER_END); OUT_RELOC(bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0); ADVANCE_BATCH(); intel_batchbuffer_flush(batch); } static void find_and_open_devices(void) { int i; char path[80], *unused; struct stat buf; FILE *fl; char vendor_id[8] = {}; int venid; for (i = 0; i < 9; i++) { sprintf(path, "/sys/class/drm/card%d/device/vendor", i); if (stat(path, &buf)) break; fl = fopen(path, "r"); if (!fl) break; unused = fgets(vendor_id, sizeof(vendor_id)-1, fl); (void)unused; fclose(fl); venid = strtoul(vendor_id, NULL, 16); sprintf(path, "/dev/dri/card%d", i); if (venid == 0x8086) { intel_fd = open(path, O_RDWR); igt_assert(intel_fd); } else if (venid == 0x10de) { nouveau_fd = open(path, O_RDWR); igt_assert(nouveau_fd); } } } static void init_nouveau(void) { struct nv04_fifo nv04_data = { .vram = 0xbeef0201, .gart = 0xbeef0202 }; struct nvc0_fifo nvc0_data = { }; struct nouveau_fifo *fifo; int size; uint32_t class; void *data; igt_assert(nouveau_device_wrap(nouveau_fd, 0, &ndev) == 0); igt_assert(nouveau_client_new(ndev, &nclient) == 0); igt_skip_on_f(ndev->chipset < 0xa3 || ndev->chipset == 0xaa || ndev->chipset == 0xac, "Your card doesn't support PCOPY\n"); // TODO: Get a kepler and add support for it igt_skip_on_f(ndev->chipset >= 0xe0, "Unsure how kepler works!\n"); igt_assert(nouveau_bo_new(ndev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 4096, 4096, NULL, &query_bo) == 0); igt_assert(nouveau_bo_map(query_bo, NOUVEAU_BO_RDWR, nclient) == 0); query = query_bo->map; *query = query_counter; if (ndev->chipset < 0xc0) { class = 0x85b5; data = &nv04_data; size = sizeof(nv04_data); } else { class = ndev->chipset < 0xe0 ? 0x490b5 : 0xa0b5; data = &nvc0_data; size = sizeof(nvc0_data); } igt_assert(nouveau_object_new(&ndev->object, 0, NOUVEAU_FIFO_CHANNEL_CLASS, data, size, &nchannel) == 0); fifo = nchannel->data; igt_assert(nouveau_pushbuf_new(nclient, nchannel, 4, 32 * 1024, true, &npush) == 0); igt_assert(nouveau_bufctx_new(nclient, 1, &nbufctx) == 0); npush->user_priv = nbufctx; /* Hope this is enough init for PCOPY */ igt_assert(nouveau_object_new(nchannel, class, class & 0xffff, NULL, 0, &pcopy) == 0); igt_assert(nouveau_pushbuf_space(npush, 512, 0, 0) == 0); if (ndev->chipset < 0xc0) { struct nv04_fifo *nv04_fifo = (struct nv04_fifo*)fifo; tile_intel_y = 0x3e; tile_intel_x = 0x13; BEGIN_NV04(npush, NV01_SUBC(COPY, OBJECT), 1); PUSH_DATA(npush, pcopy->handle); BEGIN_NV04(npush, SUBC_COPY(0x0180), 3); PUSH_DATA(npush, nv04_fifo->vram); PUSH_DATA(npush, nv04_fifo->vram); PUSH_DATA(npush, nv04_fifo->vram); } else { tile_intel_y = 0x2e; tile_intel_x = 0x03; BEGIN_NVC0(npush, NV01_SUBC(COPY, OBJECT), 1); PUSH_DATA(npush, pcopy->handle); } nouveau_pushbuf_kick(npush, npush->channel); } static void fill16(void *ptr, uint32_t val) { uint32_t *p = ptr; val = (val) | (val << 8) | (val << 16) | (val << 24); p[0] = p[1] = p[2] = p[3] = val; } #define TILE_SIZE 4096 static void swtile_y(uint8_t *out, const uint8_t *in, int w, int h) { uint32_t x, y, dx, dy; uint8_t *endptr = out + w * h; igt_assert(!(w % 128)); igt_assert(!(h % 32)); for (y = 0; y < h; y += 32) { for (x = 0; x < w; x += 128, out += TILE_SIZE) { for (dx = 0; dx < 8; ++dx) { for (dy = 0; dy < 32; ++dy) { uint32_t out_ofs = (dx * 32 + dy) * 16; uint32_t in_ofs = (y + dy) * w + (x + 16 * dx); igt_assert(out_ofs < TILE_SIZE); igt_assert(in_ofs < w*h); // To do the Y tiling quirk: // out_ofs = out_ofs ^ (((out_ofs >> 9) & 1) << 6); memcpy(&out[out_ofs], &in[in_ofs], 16); } } } } igt_assert(out == endptr); } static void swtile_x(uint8_t *out, const uint8_t *in, int w, int h) { uint32_t x, y, dy; uint8_t *endptr = out + w * h; igt_assert(!(w % 512)); igt_assert(!(h % 8)); for (y = 0; y < h; y += 8) { for (x = 0; x < w; x += 512, out += TILE_SIZE) { for (dy = 0; dy < 8; ++dy) { uint32_t out_ofs = 512 * dy; uint32_t in_ofs = (y + dy) * w + x; igt_assert(out_ofs < TILE_SIZE); igt_assert(in_ofs < w*h); memcpy(&out[out_ofs], &in[in_ofs], 512); } } } igt_assert(out == endptr); } static void perform_copy(struct nouveau_bo *nvbo, const rect *dst, uint32_t dst_x, uint32_t dst_y, struct nouveau_bo *nvbi, const rect *src, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h) { struct nouveau_pushbuf_refn refs[] = { { nvbi, (nvbi->flags & NOUVEAU_BO_APER) | NOUVEAU_BO_RD }, { nvbo, (nvbo->flags & NOUVEAU_BO_APER) | NOUVEAU_BO_WR }, { query_bo, NOUVEAU_BO_GART | NOUVEAU_BO_RDWR } }; uint32_t cpp = 1, exec = 0x00003000; /* QUERY|QUERY_SHORT|FORMAT */ uint32_t src_off = 0, dst_off = 0; struct nouveau_pushbuf *push = npush; int ret; if (nvbi->config.nv50.tile_mode == tile_intel_y) igt_debug("src is y-tiled\n"); if (nvbo->config.nv50.tile_mode == tile_intel_y) igt_debug("dst is y-tiled\n"); igt_assert(nouveau_pushbuf_space(push, 64, 0, 0) == 0); igt_assert(nouveau_pushbuf_refn(push, refs, 3) == 0); if (!nvbi->config.nv50.tile_mode) { src_off = src_y * src->pitch + src_x; exec |= 0x00000010; } if (!nvbo->config.nv50.tile_mode) { dst_off = dst_y * dst->pitch + dst_x; exec |= 0x00000100; } BEGIN_NVXX(push, SUBC_COPY(0x0200), 7); PUSH_DATA (push, nvbi->config.nv50.tile_mode); PUSH_DATA (push, src->pitch / cpp); PUSH_DATA (push, src->h); PUSH_DATA (push, 1); PUSH_DATA (push, 0); PUSH_DATA (push, src_x / cpp); PUSH_DATA (push, src_y); BEGIN_NVXX(push, SUBC_COPY(0x0220), 7); PUSH_DATA (push, nvbo->config.nv50.tile_mode); PUSH_DATA (push, dst->pitch / cpp); PUSH_DATA (push, dst->h); PUSH_DATA (push, 1); PUSH_DATA (push, 0); PUSH_DATA (push, dst_x / cpp); PUSH_DATA (push, dst_y); BEGIN_NVXX(push, SUBC_COPY(0x030c), 9); PUSH_DATA (push, (nvbi->offset + src_off) >> 32); PUSH_DATA (push, (nvbi->offset + src_off)); PUSH_DATA (push, (nvbo->offset + dst_off) >> 32); PUSH_DATA (push, (nvbo->offset + dst_off)); PUSH_DATA (push, src->pitch); PUSH_DATA (push, dst->pitch); PUSH_DATA (push, w / cpp); PUSH_DATA (push, h); PUSH_DATA (push, 0x03333120); BEGIN_NVXX(push, SUBC_COPY(0x0338), 3); PUSH_DATA (push, (query_bo->offset) >> 32); PUSH_DATA (push, (query_bo->offset)); PUSH_DATA (push, ++query_counter); BEGIN_NVXX(push, SUBC_COPY(0x0300), 1); PUSH_DATA (push, exec); ret = nouveau_pushbuf_kick(push, push->channel); while (!ret && *query < query_counter) { usleep(1000); } igt_assert_eq(ret, 0); } static void check1_macro(uint32_t *p, uint32_t w, uint32_t h) { uint32_t i, val, j; for (i = 0; i < 256; ++i, p += 4) { val = (i) | (i << 8) | (i << 16) | (i << 24); igt_assert_f(p[0] == val && p[1] == val && p[2] == val && p[3] == val, "Retile check failed in first tile!\n" "%08x %08x %08x %08x instead of %08x\n", p[0], p[1], p[2], p[3], val); } val = 0x3e3e3e3e; for (i = 0; i < 256 * (w-1); ++i, p += 4) { igt_assert_f(p[0] == val && p[1] == val && p[2] == val && p[3] == val, "Retile check failed in second tile!\n" "%08x %08x %08x %08x instead of %08x\n", p[0], p[1], p[2], p[3], val); } for (j = 1; j < h; ++j) { val = 0x7e7e7e7e; for (i = 0; i < 256; ++i, p += 4) { igt_assert_f(p[0] == val && p[1] == val && p[2] == val && p[3] == val, "Retile check failed in third tile!\n" "%08x %08x %08x %08x instead of %08x\n", p[0], p[1], p[2], p[3], val); } val = 0xcececece; for (i = 0; i < 256 * (w-1); ++i, p += 4) { igt_assert_f(p[0] == val && p[1] == val && p[2] == val && p[3] == val, "Retile check failed in fourth tile!\n" "%08x %08x %08x %08x instead of %08x\n", p[0], p[1], p[2], p[3], val); } } } /* test 1, see if we can copy from linear to intel Y format safely */ static void test1_macro(void) { int prime_fd = -1; struct nouveau_bo *nvbo = NULL, *nvbi = NULL; rect dst, src; uint8_t *ptr; uint32_t w = 2 * 128, h = 2 * 32, x, y; nv_bo_alloc(&nvbi, &src, w, h, 0, -1, NOUVEAU_BO_GART); nv_bo_alloc(&nvbo, &dst, w, h, tile_intel_y, -1, NOUVEAU_BO_GART); nouveau_bo_set_prime(nvbo, &prime_fd); /* Set up something for our tile that should map into the first * y-major tile, assuming my understanding of documentation is * correct */ /* First tile should be read out in groups of 16 bytes that * are all set to a linear increasing value.. */ ptr = nvbi->map; for (x = 0; x < 128; x += 16) for (y = 0; y < 32; ++y) fill16(&ptr[y * w + x], x * 2 + y); /* second tile */ for (x = 128; x < w; x += 16) for (y = 0; y < 32; ++y) fill16(&ptr[y * w + x], 0x3e); /* third tile */ for (x = 0; x < 128; x += 16) for (y = 32; y < h; ++y) fill16(&ptr[y * w + x], 0x7e); /* last tile */ for (x = 128; x < w; x += 16) for (y = 32; y < h; ++y) fill16(&ptr[y * w + x], 0xce); memset(nvbo->map, 0xfc, w * h); if (pcopy) perform_copy(nvbo, &dst, 0, 0, nvbi, &src, 0, 0, w, h); else swtile_y(nvbo->map, nvbi->map, w, h); check1_macro(nvbo->map, w/128, h/32); nouveau_bo_ref(NULL, &nvbo); nouveau_bo_ref(NULL, &nvbi); close(prime_fd); } static void dump_line(uint8_t *map) { uint32_t dx, dy; igt_debug("Dumping sub-tile:\n"); for (dy = 0; dy < 32; ++dy) { for (dx = 0; dx < 15; ++dx, ++map) { igt_debug("%02x ", *map); } igt_debug("%02x\n", *(map++)); } } static void check1_micro(void *map, uint32_t pitch, uint32_t lines, uint32_t dst_x, uint32_t dst_y, uint32_t w, uint32_t h) { uint32_t x, y; /* check only the relevant subrectangle [0..w) [0...h) */ uint8_t *m = map; for (y = 0; y < h; ++y, m += pitch) { for (x = 0; x < w; ++x) { uint8_t expected = ((y & 3) << 6) | (x & 0x3f); if (expected != m[x]) dump_line(m); igt_assert_f(expected == m[x], "failed check at x=%u y=%u, expected %02x got %02x\n", x, y, expected, m[x]); } } } /* test 1, but check micro format, should be unaffected by bit9 swizzling */ static void test1_micro(void) { struct nouveau_bo *bo_intel = NULL, *bo_nvidia = NULL, *bo_linear = NULL; rect intel, nvidia, linear; uint32_t tiling = I915_TILING_Y; uint32_t src_x = 0, src_y = 0; uint32_t dst_x = 0, dst_y = 0; uint32_t x, y, w = 256, h = 64; drm_intel_bo *test_intel_bo; int prime_fd; test_intel_bo = drm_intel_bo_alloc(bufmgr, "test bo", w * h, 4096); igt_assert(test_intel_bo); drm_intel_bo_set_tiling(test_intel_bo, &tiling, w); igt_assert(tiling == I915_TILING_Y); igt_assert(drm_intel_gem_bo_map_gtt(test_intel_bo) == 0); drm_intel_bo_gem_export_to_prime(test_intel_bo, &prime_fd); igt_assert_lte(0, prime_fd); noop_intel(test_intel_bo); nv_bo_alloc(&bo_intel, &intel, w, h, tile_intel_y, prime_fd, 0); nv_bo_alloc(&bo_nvidia, &nvidia, w, h, 0x10, -1, NOUVEAU_BO_VRAM); nv_bo_alloc(&bo_linear, &linear, w, h, 0, -1, NOUVEAU_BO_GART); for (y = 0; y < linear.h; ++y) { uint8_t *map = bo_linear->map; map += y * linear.pitch; for (x = 0; x < linear.pitch; ++x) { uint8_t pos = x & 0x3f; /* low 4 bits: micro tile pos */ /* 2 bits: x pos in tile (wraps) */ /* 2 bits: y pos in tile (wraps) */ pos |= (y & 3) << 6; map[x] = pos; } } perform_copy(bo_nvidia, &nvidia, 0, 0, bo_linear, &linear, 0, 0, nvidia.pitch, nvidia.h); /* Perform the actual sub rectangle copy */ if (pcopy) perform_copy(bo_intel, &intel, dst_x, dst_y, bo_nvidia, &nvidia, src_x, src_y, w, h); else swtile_y(test_intel_bo->virtual, bo_linear->map, w, h); noop_intel(test_intel_bo); check1_micro(test_intel_bo->virtual, intel.pitch, intel.h, dst_x, dst_y, w, h); nouveau_bo_ref(NULL, &bo_linear); nouveau_bo_ref(NULL, &bo_nvidia); nouveau_bo_ref(NULL, &bo_intel); drm_intel_bo_unreference(test_intel_bo); } /* test 2, see if we can copy from linear to intel X format safely * Seems nvidia lacks a method to do it, so just keep this test * as a reference for potential future tests. Software tiling is * used for now */ static void test2(void) { struct nouveau_bo *nvbo = NULL, *nvbi = NULL; rect dst, src; uint8_t *ptr; uint32_t w = 1024, h = 16, x, y; nv_bo_alloc(&nvbi, &src, w, h, 0, -1, NOUVEAU_BO_GART); nv_bo_alloc(&nvbo, &dst, w, h, tile_intel_x, -1, NOUVEAU_BO_GART); /* Set up something for our tile that should map into the first * y-major tile, assuming my understanding of documentation is * correct */ /* First tile should be read out in groups of 16 bytes that * are all set to a linear increasing value.. */ ptr = nvbi->map; for (y = 0; y < 8; ++y) for (x = 0; x < 512; x += 16) fill16(&ptr[y * w + x], (y * 512 + x)/16); for (y = 0; y < 8; ++y) for (x = 512; x < w; x += 16) fill16(&ptr[y * w + x], 0x3e); for (y = 8; y < h; ++y) for (x = 0; x < 512; x += 16) fill16(&ptr[y * w + x], 0x7e); for (y = 8; y < h; ++y) for (x = 512; x < w; x += 16) fill16(&ptr[y * w + x], 0xce); memset(nvbo->map, 0xfc, w * h); /* do this in software, there is no X major tiling in PCOPY (yet?) */ if (0 && pcopy) perform_copy(nvbo, &dst, 0, 0, nvbi, &src, 0, 0, w, h); else swtile_x(nvbo->map, nvbi->map, w, h); check1_macro(nvbo->map, w/512, h/8); nouveau_bo_ref(NULL, &nvbo); nouveau_bo_ref(NULL, &nvbi); } static void check3(const uint32_t *p, uint32_t pitch, uint32_t lines, uint32_t sub_x, uint32_t sub_y, uint32_t sub_w, uint32_t sub_h) { uint32_t x, y; sub_w += sub_x; sub_h += sub_y; igt_assert_f(p[pitch * lines / 4 - 1] != 0x03030303, "copy failed: Not all lines have been copied back!\n"); for (y = 0; y < lines; ++y) { for (x = 0; x < pitch; x += 4, ++p) { uint32_t expected; if ((x < sub_x || x >= sub_w) || (y < sub_y || y >= sub_h)) expected = 0x80808080; else expected = 0x04040404; igt_assert_f(*p == expected, "%u,%u should be %08x, but is %08x\n", x, y, expected, *p); } } } /* copy from nvidia bo to intel bo and copy to a linear bo to check if tiling went succesful */ static void test3_base(int tile_src, int tile_dst) { struct nouveau_bo *bo_intel = NULL, *bo_nvidia = NULL, *bo_linear = NULL; rect intel, nvidia, linear; uint32_t cpp = 4; uint32_t src_x = 1 * cpp, src_y = 1; uint32_t dst_x = 2 * cpp, dst_y = 26; uint32_t w = 298 * cpp, h = 298; drm_intel_bo *test_intel_bo; int prime_fd; test_intel_bo = drm_intel_bo_alloc(bufmgr, "test bo", 2048 * cpp * 768, 4096); igt_assert(test_intel_bo); drm_intel_bo_gem_export_to_prime(test_intel_bo, &prime_fd); igt_assert_lte(0, prime_fd); nv_bo_alloc(&bo_intel, &intel, 2048 * cpp, 768, tile_dst, prime_fd, 0); nv_bo_alloc(&bo_nvidia, &nvidia, 300 * cpp, 300, tile_src, -1, NOUVEAU_BO_VRAM); nv_bo_alloc(&bo_linear, &linear, 2048 * cpp, 768, 0, -1, NOUVEAU_BO_GART); noop_intel(test_intel_bo); memset(bo_linear->map, 0x80, bo_linear->size); perform_copy(bo_intel, &intel, 0, 0, bo_linear, &linear, 0, 0, linear.pitch, linear.h); noop_intel(test_intel_bo); memset(bo_linear->map, 0x04, bo_linear->size); perform_copy(bo_nvidia, &nvidia, 0, 0, bo_linear, &linear, 0, 0, nvidia.pitch, nvidia.h); /* Perform the actual sub rectangle copy */ noop_intel(test_intel_bo); perform_copy(bo_intel, &intel, dst_x, dst_y, bo_nvidia, &nvidia, src_x, src_y, w, h); noop_intel(test_intel_bo); memset(bo_linear->map, 0x3, bo_linear->size); noop_intel(test_intel_bo); perform_copy(bo_linear, &linear, 0, 0, bo_intel, &intel, 0, 0, intel.pitch, intel.h); noop_intel(test_intel_bo); check3(bo_linear->map, linear.pitch, linear.h, dst_x, dst_y, w, h); nouveau_bo_ref(NULL, &bo_linear); nouveau_bo_ref(NULL, &bo_nvidia); nouveau_bo_ref(NULL, &bo_intel); drm_intel_bo_unreference(test_intel_bo); } static void test3_1(void) { /* nvidia tiling to intel */ test3_base(0x40, tile_intel_y); } static void test3_2(void) { /* intel tiling to nvidia */ test3_base(tile_intel_y, 0x40); } static void test3_3(void) { /* intel tiling to linear */ test3_base(tile_intel_y, 0); } static void test3_4(void) { /* linear tiling to intel */ test3_base(0, tile_intel_y); } static void test3_5(void) { /* linear to linear */ test3_base(0, 0); } /* Acquire when == SEQUENCE */ #define SEMA_ACQUIRE_EQUAL 1 /* Release, and write a 16 byte query structure to sema: * { (uint32)seq, (uint32)0, (uint64)timestamp } */ #define SEMA_WRITE_LONG 2 /* Acquire when >= SEQUENCE */ #define SEMA_ACQUIRE_GEQUAL 4 /* Test only new style semaphores, old ones are AWFUL */ static void test_semaphore(void) { drm_intel_bo *test_intel_bo = NULL; struct nouveau_bo *sema_bo = NULL; int prime_fd; uint32_t *sema; struct nouveau_pushbuf *push = npush; igt_skip_on(ndev->chipset < 0x84); /* Should probably be kept in sysmem */ test_intel_bo = drm_intel_bo_alloc(bufmgr, "semaphore bo", 4096, 4096); igt_assert(test_intel_bo); drm_intel_bo_gem_export_to_prime(test_intel_bo, &prime_fd); igt_assert_lte(0, prime_fd); igt_assert(nouveau_bo_prime_handle_ref(ndev, prime_fd, &sema_bo) == 0); close(prime_fd); igt_assert(drm_intel_gem_bo_map_gtt(test_intel_bo) == 0); sema = test_intel_bo->virtual; sema++; *sema = 0; igt_assert(nouveau_pushbuf_space(push, 64, 0, 0) == 0); igt_assert(nouveau_pushbuf_refn(push, &(struct nouveau_pushbuf_refn) { sema_bo, NOUVEAU_BO_GART|NOUVEAU_BO_RDWR }, 1) == 0); if (ndev->chipset < 0xc0) { struct nv04_fifo *nv04_fifo = nchannel->data; /* kernel binds it's own dma object here and overwrites old one, * so just rebind vram every time we submit */ BEGIN_NV04(npush, SUBC_COPY(0x0060), 1); PUSH_DATA(npush, nv04_fifo->vram); } BEGIN_NVXX(push, SUBC_COPY(0x0010), 4); PUSH_DATA(push, sema_bo->offset >> 32); PUSH_DATA(push, sema_bo->offset + 4); PUSH_DATA(push, 2); // SEQUENCE PUSH_DATA(push, SEMA_WRITE_LONG); // TRIGGER BEGIN_NVXX(push, SUBC_COPY(0x0018), 2); PUSH_DATA(push, 3); PUSH_DATA(push, SEMA_ACQUIRE_EQUAL); BEGIN_NVXX(push, SUBC_COPY(0x0018), 2); PUSH_DATA(push, 4); PUSH_DATA(push, SEMA_WRITE_LONG); BEGIN_NVXX(push, SUBC_COPY(0x0018), 2); PUSH_DATA(push, 5); PUSH_DATA(push, SEMA_ACQUIRE_GEQUAL); BEGIN_NVXX(push, SUBC_COPY(0x0018), 2); PUSH_DATA(push, 6); PUSH_DATA(push, SEMA_WRITE_LONG); BEGIN_NVXX(push, SUBC_COPY(0x0018), 2); PUSH_DATA(push, 7); PUSH_DATA(push, SEMA_ACQUIRE_GEQUAL); BEGIN_NVXX(push, SUBC_COPY(0x0018), 2); PUSH_DATA(push, 9); PUSH_DATA(push, SEMA_WRITE_LONG); nouveau_pushbuf_kick(push, push->channel); usleep(1000); igt_assert(*sema == 2); *sema = 3; usleep(1000); igt_assert(*sema == 4); *sema = 5; usleep(1000); igt_assert(*sema == 6); *sema = 8; usleep(1000); igt_assert(*sema == 9); nouveau_bo_ref(NULL, &sema_bo); drm_intel_bo_unreference(test_intel_bo); } igt_main { igt_fixture { find_and_open_devices(); igt_require(nouveau_fd != -1); igt_require(intel_fd != -1); /* set up intel bufmgr */ bufmgr = drm_intel_bufmgr_gem_init(intel_fd, 4096); igt_assert(bufmgr); /* Do not enable reuse, we share (almost) all buffers. */ //drm_intel_bufmgr_gem_enable_reuse(bufmgr); /* set up nouveau bufmgr */ init_nouveau(); /* set up an intel batch buffer */ devid = intel_get_drm_devid(intel_fd); batch = intel_batchbuffer_alloc(bufmgr, devid); igt_assert(batch); } #define xtest(x, args...) \ igt_subtest( #x ) \ (x)(args); xtest(test1_macro); xtest(test1_micro); //xtest(test1_swizzle); xtest(test2); xtest(test3_1); xtest(test3_2); xtest(test3_3); xtest(test3_4); xtest(test3_5); xtest(test_semaphore); igt_fixture { nouveau_bo_ref(NULL, &query_bo); nouveau_object_del(&pcopy); nouveau_bufctx_del(&nbufctx); nouveau_pushbuf_del(&npush); nouveau_object_del(&nchannel); intel_batchbuffer_free(batch); nouveau_client_del(&nclient); nouveau_device_del(&ndev); drm_intel_bufmgr_destroy(bufmgr); close(intel_fd); close(nouveau_fd); } }