rendered paste body#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <xf86drm.h>
#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;
}