#include #include #include #include #include #include #include "nouveau_drm.h" #include "nouveau_device.h" #include "nouveau_notifier.h" #include "nouveau_channel.h" #include "nouveau_pushbuf.h" #include "nouveau_bo.h" #include "nouveau_class.h" #define NvDmaFB 0xD0FB0001 #define NvDmaTT 0xD0AA0001 #define Nv2D 0xD02D0001 #define NvM2MF 0xD02D0002 #define NvNotify 0xD0000001 struct nouveau_device *dev = NULL; struct nouveau_channel *chan = NULL; struct nouveau_notifier *notifier = NULL; struct nouveau_grobj *eng2d = NULL; struct nouveau_grobj *m2mf = NULL; struct nouveau_bo *bo = NULL; struct nouveau_bo *gart = NULL; #define NV84_MODE 0 #define NV90_MODE 0 #define WIDTH 400 #define HEIGHT 400 #define TILING_PARAM 0x40 #if TILING_PARAM == 0x00 #define TILE_WIDTH 64 #define TILE_HEIGHT 4 #elif TILING_PARAM == 0x10 #define TILE_WIDTH 64 #define TILE_HEIGHT 8 #elif TILING_PARAM == 0x20 #define TILE_WIDTH 64 #define TILE_HEIGHT 16 #elif TILING_PARAM == 0x30 #define TILE_WIDTH 64 #define TILE_HEIGHT 32 #elif TILING_PARAM == 0x40 #define TILE_WIDTH 64 #define TILE_HEIGHT 64 #endif #define PITCH_WIDTH ((WIDTH + 63) & ~63) #define PITCH_HEIGHT ((HEIGHT + TILE_HEIGHT - 1) & ~(TILE_HEIGHT - 1)) void get_dst() { int ret = 0, i; uint8_t *map; if (ret = nouveau_bo_new(dev, NOUVEAU_BO_VRAM | NOUVEAU_BO_TILED | NOUVEAU_BO_MAP, 256, PITCH_WIDTH*PITCH_HEIGHT + 65536, &bo)) { fprintf(stderr, "nouveau_bo_new failed with %d\n", ret); return; } /* Set destination. */ BEGIN_RING (chan, eng2d, NV50_2D_DST_FORMAT, 5); OUT_RING (chan, NV50_2D_DST_FORMAT_R8_UNORM); OUT_RING (chan, 0); OUT_RING (chan, TILING_PARAM); OUT_RING (chan, 1); OUT_RING (chan, 0); BEGIN_RING (chan, eng2d, NV50_2D_DST_WIDTH, 4); OUT_RING (chan, WIDTH); OUT_RING (chan, HEIGHT); OUT_RELOCh(chan, bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR); OUT_RELOCl(chan, bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR); BEGIN_RING(chan, eng2d, NV50_2D_CLIP_X, 4); OUT_RING (chan, 0); OUT_RING (chan, 0); OUT_RING (chan, WIDTH); OUT_RING (chan, HEIGHT); BEGIN_RING(chan, eng2d, NV50_2D_OPERATION, 1); OUT_RING (chan, NV50_2D_OPERATION_SRCCOPY); FIRE_RING (chan); /* clear the pixmap */ nouveau_bo_map(bo, NOUVEAU_BO_RDWR); map = bo->map; if (!map) { fprintf(stderr, "map failed\n"); return; } for (i = 0; i < bo->size; i++) map[i] = 0x00; nouveau_bo_unmap(bo); } void get_gart() { int ret; if (ret = nouveau_bo_new(dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 256, PITCH_WIDTH*PITCH_HEIGHT + 65536, &gart)) { fprintf(stderr, "nouveau_bo_new failed with %d\n", ret); return; } } int recurse_offset(int tile_height, int x, int y, int pitch) { int tile_width = TILE_WIDTH; int offset; offset = ((x/tile_width) + (y/tile_height) * (pitch/tile_width)) * (tile_height * tile_width); offset += (y % tile_height) * tile_width + (x % tile_width); return offset; } void download_pixels(int x, int y, int full) { unsigned line_len; unsigned line_count; unsigned offset; uint64_t vram_offset = 0; if (full) { offset = 0; line_len = PITCH_WIDTH; line_count = PITCH_HEIGHT; } else { offset = recurse_offset(TILE_HEIGHT, x, y, PITCH_WIDTH); line_len = 1; line_count = 1; } nouveau_bo_pin(bo, NOUVEAU_BO_VRAM); if (!bo->offset) { fprintf(stderr, "no offset\n"); exit(0); } vram_offset = bo->offset - dev->vm_vram_base; fprintf(stderr, "0x%016llx\n", vram_offset); WAIT_RING (chan, 32); BEGIN_RING(chan, m2mf, 0x180, 3); OUT_RING (chan, notifier->handle); OUT_RING (chan, 0xdeadcafe); OUT_RELOCo(chan, gart, NOUVEAU_BO_GART | NOUVEAU_BO_WR); BEGIN_RING(chan, m2mf, 0x0200, 1); OUT_RING (chan, 1); BEGIN_RING(chan, m2mf, 0x021c, 1); OUT_RING (chan, 1); BEGIN_RING(chan, m2mf, 0x238, 2); OUT_RING(chan, vram_offset >> 32); OUT_RELOCh(chan, gart, offset, NOUVEAU_BO_GART | NOUVEAU_BO_WR); BEGIN_RING(chan, m2mf, NV04_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); OUT_RING(chan, vram_offset & 0xFFFFFFFF); OUT_RELOCl(chan, gart, offset, NOUVEAU_BO_GART | NOUVEAU_BO_WR); OUT_RING (chan, PITCH_WIDTH); OUT_RING (chan, PITCH_WIDTH); OUT_RING (chan, line_len); OUT_RING (chan, line_count); OUT_RING (chan, (1<<8)|1); OUT_RING (chan, 0); FIRE_RING (chan); nouveau_bo_unpin(bo); } void set_pixel(int x, int y, uint32_t pixel) { BEGIN_RING (chan, eng2d, NV50_2D_DST_ADDRESS_HIGH, 2); OUT_RELOCh(chan, bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR); OUT_RELOCl(chan, bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR); BEGIN_RING(chan, eng2d, 0x580, 3); OUT_RING (chan, 4); OUT_RING (chan, NV50_2D_RECT_FORMAT_R8_UNORM); OUT_RING (chan, pixel); BEGIN_RING(chan, eng2d, NV50_2D_RECT_X1, 4); OUT_RING (chan, x); OUT_RING (chan, y); OUT_RING (chan, x + 1); OUT_RING (chan, y + 1); FIRE_RING (chan); } void find_pixel (int x, int y, int *offset, uint32_t pixel) { uint8_t *map = NULL; int smart_offset = *offset - 2048; int smart_end = smart_offset + 4096; if (smart_offset < 0) smart_offset = 0; if (smart_end > gart->size) smart_end = gart->size; if (x == 0) fprintf(stderr, "y% d\n", y); download_pixels(x, y, 0); nouveau_bo_map(gart, NOUVEAU_BO_RDWR); map = gart->map; if (!map) { fprintf(stderr, "map failed\n"); exit(0); }; if (map[*offset] == (pixel & 0xFF)) goto out; fprintf(stderr, "fail\n"); nouveau_bo_unmap(gart); download_pixels(x, y, 1); nouveau_bo_map(gart, NOUVEAU_BO_RDWR); map = gart->map; if (!map) { fprintf(stdout, "map failed\n"); exit(0); }; for ((*offset) = smart_offset; (*offset) < smart_end; (*offset)++) if ((map[(*offset)]) == ((pixel) & 0xFF)) goto out; for ((*offset) = 0; (*offset) < gart->size; (*offset)++) if ((map[(*offset)]) == ((pixel) & 0xFF)) goto out; fprintf(stderr, "really fail\n"); out: nouveau_bo_unmap(gart); } int main(int argc, char **argv) { int ret = 0; int x = 0, y = 0; int offset = 0, offset_expected = 0; if (ret = nouveau_device_open(&dev, NULL)) { fprintf(stderr, "nouveau_device_open failed\n"); return ret; } if (ret = nouveau_channel_alloc(dev, NvDmaFB, NvDmaTT, &chan)) { fprintf(stderr, "nouveau_channel_alloc failed\n"); return ret; } if (ret = nouveau_notifier_alloc(chan, NvNotify, 1, ¬ifier)) { fprintf(stderr, "nouveau_notifier_alloc failed\n"); return ret; } /* 2D object */ if (ret = nouveau_grobj_alloc(chan, Nv2D, 0x502d, &eng2d)) { fprintf(stderr, "nouveau_grobj_alloc failed\n"); return ret; } if (ret = nouveau_grobj_alloc(chan, NvM2MF, NV50_MEMORY_TO_MEMORY_FORMAT, &m2mf)) { fprintf(stderr, "nouveau_grobj_alloc failed\n"); return ret; } BEGIN_RING (chan, eng2d, 0x180, 3); OUT_RING (chan, notifier->handle); OUT_RING (chan, chan->vram->handle); OUT_RING (chan, chan->gart->handle); /* Magics from nv, no clue what they do, but at least some * of them are needed to avoid crashes. */ BEGIN_RING(chan, eng2d, 0x260, 1); OUT_RING (chan, 1); BEGIN_RING(chan, eng2d, 0x290, 1); OUT_RING (chan, 1); BEGIN_RING(chan, eng2d, 0x29c, 1); OUT_RING (chan, 0); BEGIN_RING(chan, eng2d, 0x58c, 1); OUT_RING (chan, 0x111); /* Now we are ready for the real stuff. */ get_dst(); if (!bo) { fprintf(stderr, "get_dst failed\n"); return -ENOMEM; } get_gart(); if (!gart) { fprintf(stderr, "get_gart failed\n"); return -ENOMEM; } for (y = 0; y < HEIGHT; y++) { for (x = 0; x < WIDTH; x++) { set_pixel(x, y, 0xFFFFFFFF); offset_expected = recurse_offset(TILE_HEIGHT, x, y, PITCH_WIDTH); offset = offset_expected; find_pixel(x, y, &offset, 0xFFFFFFFF); set_pixel(x, y, 0x0); if (offset_expected != offset) { printf("offset %d offset_expected %d diff %d \n", offset, offset_expected, offset_expected - offset); printf("x %d y %d\n", x, y); } } } nouveau_device_close(&dev); return ret; }