/* 
 * Copyright © 2008 Nicolai Haehnle
 * Copyright © 2008 Dave Airlie
 * Copyright © 2008 Jérôme Glisse
 * All Rights Reserved.
 * 
 * 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 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 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS 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.
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 */
/*
 * Authors:
 *      Aapo Tahkola <aet@rasterburn.org>
 *      Nicolai Haehnle <prefect_@gmx.net>
 *      Dave Airlie
 *      Jérôme Glisse <glisse@freedesktop.org>
 */
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "xf86drm.h"
#include "texmem.h"
#include "main/simple_list.h"

#include "drm.h"
#include "radeon_drm.h"
#include "radeon_common.h"
#include "radeon_bocs_wrapper.h"
#include "radeon_macros.h"

#ifdef HAVE_LIBDRM_RADEON
#include "radeon_bo_int.h"
#else
#include "radeon_bo_int_drm.h"
#endif

/* no seriously texmem.c is this screwed up */
struct bo_legacy_texture_object {
    driTextureObject    base;
    struct bo_legacy *parent;
};

struct bo_legacy {
    struct radeon_bo_int    base;
    int                 map_count;
    uint32_t            pending;
    int                 is_pending;
    int                 static_bo;
    uint32_t            offset;
    struct bo_legacy_texture_object *tobj;
    int                 validated;
    int                 dirty;
    void                *ptr;
    struct bo_legacy    *next, *prev;
    struct bo_legacy    *pnext, *pprev;
};

struct bo_manager_legacy {
    struct radeon_bo_manager    base;
    unsigned                    nhandle;
    unsigned                    nfree_handles;
    unsigned                    cfree_handles;
    uint32_t                    current_age;
    struct bo_legacy            bos;
    struct bo_legacy            pending_bos;
    uint32_t                    fb_location;
    uint32_t                    texture_offset;
    unsigned                    dma_alloc_size;
    uint32_t                    dma_buf_count;
    unsigned                    cpendings;
    driTextureObject            texture_swapped;
    driTexHeap                  *texture_heap;
    struct radeon_screen        *screen;
    unsigned                    *free_handles;
};

static void bo_legacy_tobj_destroy(void *data, driTextureObject *t)
{
    struct bo_legacy_texture_object *tobj = (struct bo_legacy_texture_object *)t;
    
    if (tobj->parent) {
        tobj->parent->tobj = NULL;
        tobj->parent->validated = 0;
    }
}

static void inline clean_handles(struct bo_manager_legacy *bom)
{
  while (bom->cfree_handles > 0 &&
	 !bom->free_handles[bom->cfree_handles - 1])
    bom->cfree_handles--;

}
static int legacy_new_handle(struct bo_manager_legacy *bom, uint32_t *handle)
{
    uint32_t tmp;

    *handle = 0;
    if (bom->nhandle == 0xFFFFFFFF) {
        return -EINVAL;
    }
    if (bom->cfree_handles > 0) {
        tmp = bom->free_handles[--bom->cfree_handles];
	clean_handles(bom);
    } else {
        bom->cfree_handles = 0;
        tmp = bom->nhandle++;
    }
    assert(tmp);
    *handle = tmp;
    return 0;
}

static int legacy_free_handle(struct bo_manager_legacy *bom, uint32_t handle)
{
    uint32_t *handles;

    if (!handle) {
        return 0;
    }
    if (handle == (bom->nhandle - 1)) {
        int i;

        bom->nhandle--;
        for (i = bom->cfree_handles - 1; i >= 0; i--) {
            if (bom->free_handles[i] == (bom->nhandle - 1)) {
                bom->nhandle--;
                bom->free_handles[i] = 0;
            }
        }
        clean_handles(bom);
        return 0;
    }
    if (bom->cfree_handles < bom->nfree_handles) {
        bom->free_handles[bom->cfree_handles++] = handle;
        return 0;
    }
    bom->nfree_handles += 0x100;
    handles = (uint32_t*)realloc(bom->free_handles, bom->nfree_handles * 4);
    if (handles == NULL) {
        bom->nfree_handles -= 0x100;
        return -ENOMEM;
    }
    bom->free_handles = handles;
    bom->free_handles[bom->cfree_handles++] = handle;
    return 0;
}

static void legacy_get_current_age(struct bo_manager_legacy *boml)
{
    drm_radeon_getparam_t gp;
    unsigned char *RADEONMMIO = NULL;
    int r;

    if (   IS_R300_CLASS(boml->screen) 
        || IS_R600_CLASS(boml->screen) ) 
    {
    	gp.param = RADEON_PARAM_LAST_CLEAR;
    	gp.value = (int *)&boml->current_age;
    	r = drmCommandWriteRead(boml->base.fd, DRM_RADEON_GETPARAM,
       	                     &gp, sizeof(gp));
    	if (r) {
       	 fprintf(stderr, "%s: drmRadeonGetParam: %d\n", __FUNCTION__, r);
         exit(1);
       }
    } 
    else {
        RADEONMMIO = boml->screen->mmio.map;
        boml->current_age = boml->screen->scratch[3];
        boml->current_age = INREG(RADEON_GUI_SCRATCH_REG3);
    }
}

static int legacy_is_pending(struct radeon_bo_int *boi)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)boi->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)boi;

    if (bo_legacy->is_pending <= 0) {
        bo_legacy->is_pending = 0;
        return 0;
    }
    if (boml->current_age >= bo_legacy->pending) {
        if (boml->pending_bos.pprev == bo_legacy) {
            boml->pending_bos.pprev = bo_legacy->pprev;
        }
        bo_legacy->pprev->pnext = bo_legacy->pnext;
        if (bo_legacy->pnext) {
            bo_legacy->pnext->pprev = bo_legacy->pprev;
        }
	assert(bo_legacy->is_pending <= boi->cref);
        while (bo_legacy->is_pending--) {
	    boi = (struct radeon_bo_int *)radeon_bo_unref((struct radeon_bo *)boi);
	    if (!boi)
	      break;
        }
	if (boi)
	  bo_legacy->is_pending = 0;
        boml->cpendings--;
        return 0;
    }
    return 1;
}

static int legacy_wait_pending(struct radeon_bo_int *bo)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bo->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;

    if (!bo_legacy->is_pending) {
        return 0;
    }
    /* FIXME: lockup and userspace busy looping that's all the folks */
    legacy_get_current_age(boml);
    while (legacy_is_pending(bo)) {
        usleep(10);
        legacy_get_current_age(boml);
    }
    return 0;
}

void legacy_track_pending(struct radeon_bo_manager *bom, int debug)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy*) bom;
    struct bo_legacy *bo_legacy;
    struct bo_legacy *next;

    legacy_get_current_age(boml);
    bo_legacy = boml->pending_bos.pnext;
    while (bo_legacy) {
        if (debug)
            fprintf(stderr,"pending %p %d %d %d\n", bo_legacy, bo_legacy->base.size,
                    boml->current_age, bo_legacy->pending);
        next = bo_legacy->pnext;
        if (legacy_is_pending(&(bo_legacy->base))) {
        }
        bo_legacy = next;
    } 
}

static int legacy_wait_any_pending(struct bo_manager_legacy *boml)
{
    struct bo_legacy *bo_legacy;

    legacy_get_current_age(boml);
    bo_legacy = boml->pending_bos.pnext;
    if (!bo_legacy)
      return -1;
    legacy_wait_pending(&bo_legacy->base);
    return 0;
}

static void legacy_kick_all_buffers(struct bo_manager_legacy *boml)
{
    struct bo_legacy *legacy;

    legacy = boml->bos.next;
    while (legacy != &boml->bos) {
	if (legacy->tobj) {
	    if (legacy->validated) {
		driDestroyTextureObject(&legacy->tobj->base);
		legacy->tobj = 0;
		legacy->validated = 0;
	    }
	}
	legacy = legacy->next;
    }
}

static struct bo_legacy *bo_allocate(struct bo_manager_legacy *boml,
                                     uint32_t size,
                                     uint32_t alignment,
                                     uint32_t domains,
                                     uint32_t flags)
{
    struct bo_legacy *bo_legacy;
    static int pgsize;

    if (pgsize == 0)
        pgsize = getpagesize() - 1;

    size = (size + pgsize) & ~pgsize;

    bo_legacy = (struct bo_legacy*)calloc(1, sizeof(struct bo_legacy));
    if (bo_legacy == NULL) {
        return NULL;
    }
    bo_legacy->base.bom = (struct radeon_bo_manager*)boml;
    bo_legacy->base.handle = 0;
    bo_legacy->base.size = size;
    bo_legacy->base.alignment = alignment;
    bo_legacy->base.domains = domains;
    bo_legacy->base.flags = flags;
    bo_legacy->base.ptr = NULL;
    bo_legacy->map_count = 0;
    bo_legacy->next = NULL;
    bo_legacy->prev = NULL;
    bo_legacy->pnext = NULL;
    bo_legacy->pprev = NULL;
    bo_legacy->next = boml->bos.next;
    bo_legacy->prev = &boml->bos;
    boml->bos.next = bo_legacy;
    if (bo_legacy->next) {
        bo_legacy->next->prev = bo_legacy;
    }

    return bo_legacy;
}

static int bo_dma_alloc(struct radeon_bo_int *bo)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bo->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;
    drm_radeon_mem_alloc_t alloc;
    unsigned size;
    int base_offset;
    int r;

    /* align size on 4Kb */
    size = (((4 * 1024) - 1) + bo_legacy->base.size) & ~((4 * 1024) - 1);
    alloc.region = RADEON_MEM_REGION_GART;
    alloc.alignment = bo_legacy->base.alignment;
    alloc.size = size;
    alloc.region_offset = &base_offset;
    r = drmCommandWriteRead(bo->bom->fd,
                            DRM_RADEON_ALLOC,
                            &alloc,
                            sizeof(alloc));
    if (r) {
        /* ptr is set to NULL if dma allocation failed */
        bo_legacy->ptr = NULL;
        return r;
    }
    bo_legacy->ptr = boml->screen->gartTextures.map + base_offset;
    bo_legacy->offset = boml->screen->gart_texture_offset + base_offset;
    bo->size = size;
    boml->dma_alloc_size += size;
    boml->dma_buf_count++;
    return 0;
}

static int bo_dma_free(struct radeon_bo_int *bo)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bo->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;
    drm_radeon_mem_free_t memfree;
    int r;

    if (bo_legacy->ptr == NULL) {
        /* ptr is set to NULL if dma allocation failed */
        return 0;
    }
    legacy_get_current_age(boml);
    memfree.region = RADEON_MEM_REGION_GART;
    memfree.region_offset  = bo_legacy->offset;
    memfree.region_offset -= boml->screen->gart_texture_offset;
    r = drmCommandWrite(boml->base.fd,
                        DRM_RADEON_FREE,
                        &memfree,
                        sizeof(memfree));
    if (r) {
        fprintf(stderr, "Failed to free bo[%p] at %08x\n",
                &bo_legacy->base, memfree.region_offset);
        fprintf(stderr, "ret = %s\n", strerror(-r));
        return r;
    }
    boml->dma_alloc_size -= bo_legacy->base.size;
    boml->dma_buf_count--;
    return 0;
}

static void bo_free(struct bo_legacy *bo_legacy)
{
    struct bo_manager_legacy *boml;

    if (bo_legacy == NULL) {
        return;
    }
    boml = (struct bo_manager_legacy *)bo_legacy->base.bom;
    bo_legacy->prev->next = bo_legacy->next;
    if (bo_legacy->next) {
        bo_legacy->next->prev = bo_legacy->prev;
    }
    if (!bo_legacy->static_bo) {
        legacy_free_handle(boml, bo_legacy->base.handle);
        if (bo_legacy->base.domains & RADEON_GEM_DOMAIN_GTT) {
            /* dma buffers */
            bo_dma_free(&bo_legacy->base);
        } else {
  	    driDestroyTextureObject(&bo_legacy->tobj->base);
	    bo_legacy->tobj = NULL;
            /* free backing store */
            free(bo_legacy->ptr);
        }
    }
    memset(bo_legacy, 0 , sizeof(struct bo_legacy));
    free(bo_legacy);
}

static struct radeon_bo *bo_open(struct radeon_bo_manager *bom,
                                 uint32_t handle,
                                 uint32_t size,
                                 uint32_t alignment,
                                 uint32_t domains,
                                 uint32_t flags)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bom;
    struct bo_legacy *bo_legacy;
    int r;

    if (handle) {
        bo_legacy = boml->bos.next;
        while (bo_legacy) {
            if (bo_legacy->base.handle == handle) {
                radeon_bo_ref((struct radeon_bo *)&(bo_legacy->base));
                return (struct radeon_bo*)bo_legacy;
            }
            bo_legacy = bo_legacy->next;
        }
        return NULL;
    }
    bo_legacy = bo_allocate(boml, size, alignment, domains, flags);
    bo_legacy->static_bo = 0;
    r = legacy_new_handle(boml, &bo_legacy->base.handle);
    if (r) {
        bo_free(bo_legacy);
        return NULL;
    }
    if (bo_legacy->base.domains & RADEON_GEM_DOMAIN_GTT) 
    {
retry:
        legacy_track_pending(&boml->base, 0);
        /* dma buffers */

        r = bo_dma_alloc(&(bo_legacy->base));
        if (r) 
        {
	         if (legacy_wait_any_pending(boml) == -1) 
             {
                  bo_free(bo_legacy);
	              return NULL;
             }
	         goto retry;
	         return NULL;
        }
    } 
    else 
    {
        bo_legacy->ptr = malloc(bo_legacy->base.size);
        if (bo_legacy->ptr == NULL) {
            bo_free(bo_legacy);
            return NULL;
        }
    }
    radeon_bo_ref((struct radeon_bo *)&(bo_legacy->base));

    return (struct radeon_bo*)bo_legacy;
}

static void bo_ref(struct radeon_bo_int *bo)
{
}

static struct radeon_bo *bo_unref(struct radeon_bo_int *boi)
{
    struct bo_legacy *bo_legacy = (struct bo_legacy*)boi;

    if (boi->cref <= 0) {
        bo_legacy->prev->next = bo_legacy->next;
        if (bo_legacy->next) {
            bo_legacy->next->prev = bo_legacy->prev;
        }
        if (!bo_legacy->is_pending) {
            bo_free(bo_legacy);
        }
        return NULL;
    }
    return (struct radeon_bo *)boi;
}

static int bo_map(struct radeon_bo_int *bo, int write)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bo->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;

    legacy_wait_pending(bo);
    bo_legacy->validated = 0;
    bo_legacy->dirty = 1;
    bo_legacy->map_count++;
    bo->ptr = bo_legacy->ptr;
    /* Read the first pixel in the frame buffer.  This should
     * be a noop, right?  In fact without this conform fails as reading
     * from the framebuffer sometimes produces old results -- the
     * on-card read cache gets mixed up and doesn't notice that the
     * framebuffer has been updated.
     *
     * Note that we should probably be reading some otherwise unused
     * region of VRAM, otherwise we might get incorrect results when
     * reading pixels from the top left of the screen.
     *
     * I found this problem on an R420 with glean's texCube test.
     * Note that the R200 span code also *writes* the first pixel in the
     * framebuffer, but I've found this to be unnecessary.
     *  -- Nicolai Hähnle, June 2008
     */
    if (!(bo->domains & RADEON_GEM_DOMAIN_GTT)) {
        int p;
        volatile int *buf = (int*)boml->screen->driScreen->pFB;
        p = *buf;
    }

    return 0;
}

static int bo_unmap(struct radeon_bo_int *bo)
{
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;

    if (--bo_legacy->map_count > 0) 
    {
        return 0;
    }
    
    bo->ptr = NULL;

    return 0;
}

static int bo_is_busy(struct radeon_bo_int *bo, uint32_t *domain)
{
    *domain = 0;
    if (bo->domains & RADEON_GEM_DOMAIN_GTT)
        *domain = RADEON_GEM_DOMAIN_GTT;
    else
        *domain = RADEON_GEM_DOMAIN_CPU;
    if (legacy_is_pending(bo))
        return -EBUSY;
    else
        return 0;
}

static int bo_is_static(struct radeon_bo_int *bo)
{
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;
    return bo_legacy->static_bo;
}

static struct radeon_bo_funcs bo_legacy_funcs = {
    bo_open,
    bo_ref,
    bo_unref,
    bo_map,
    bo_unmap,
    NULL,
    bo_is_static,
    NULL,
    NULL,
    bo_is_busy
};

static int bo_vram_validate(struct radeon_bo_int *bo,
                            uint32_t *soffset,
                            uint32_t *eoffset)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bo->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;
    int r;
    int retry_count = 0, pending_retry = 0;
    
    if (!bo_legacy->tobj) {
	bo_legacy->tobj = CALLOC(sizeof(struct bo_legacy_texture_object));
	bo_legacy->tobj->parent = bo_legacy;
	make_empty_list(&bo_legacy->tobj->base);
	bo_legacy->tobj->base.totalSize = bo->size;
    retry:
        r = driAllocateTexture(&boml->texture_heap, 1,
                               &bo_legacy->tobj->base);
        if (r) {
		pending_retry = 0;
		while(boml->cpendings && pending_retry++ < 10000) {
			legacy_track_pending(&boml->base, 0);
			retry_count++;
			if (retry_count > 2) {
				free(bo_legacy->tobj);
				bo_legacy->tobj = NULL;
				fprintf(stderr, "Ouch! vram_validate failed %d\n", r);
				return -1;
			}
			goto retry;
		}
	}
        bo_legacy->offset = boml->texture_offset +
                            bo_legacy->tobj->base.memBlock->ofs;
        bo_legacy->dirty = 1;
    }

    assert(bo_legacy->tobj->base.memBlock);

    driUpdateTextureLRU(&bo_legacy->tobj->base);

    if (bo_legacy->dirty || bo_legacy->tobj->base.dirty_images[0]) {
	    if (IS_R600_CLASS(boml->screen)) {
		    drm_radeon_texture_t tex;
		    drm_radeon_tex_image_t tmp;
		    int ret;

		    tex.offset = bo_legacy->offset;
		    tex.image = &tmp;
		    assert(!(tex.offset & 1023));

		    tmp.x = 0;
		    tmp.y = 0;
		    tmp.width = bo->size;
		    tmp.height = 1;
		    tmp.data = bo_legacy->ptr;
		    tex.format = RADEON_TXFORMAT_ARGB8888;
		    tex.width = tmp.width;
		    tex.height = tmp.height;
		    tex.pitch = bo->size;
		    do {
			    ret = drmCommandWriteRead(bo->bom->fd,
						      DRM_RADEON_TEXTURE,
						      &tex,
						      sizeof(drm_radeon_texture_t));
			    if (ret) {
				    if (RADEON_DEBUG & RADEON_IOCTL)
					    fprintf(stderr, "DRM_RADEON_TEXTURE:  again!\n");
				    usleep(1);
			    }
		    } while (ret == -EAGAIN);
	    } else {
		    /* Copy to VRAM using a blit.
		     * All memory is 4K aligned. We're using 1024 pixels wide blits.
		     */
		    drm_radeon_texture_t tex;
		    drm_radeon_tex_image_t tmp;
		    int ret;

		    tex.offset = bo_legacy->offset;
		    tex.image = &tmp;
		    assert(!(tex.offset & 1023));

		    tmp.x = 0;
		    tmp.y = 0;
		    if (bo->size < 4096) {
			    tmp.width = (bo->size + 3) / 4;
			    tmp.height = 1;
		    } else {
			    tmp.width = 1024;
			    tmp.height = (bo->size + 4095) / 4096;
		    }
		    tmp.data = bo_legacy->ptr;
		    tex.format = RADEON_TXFORMAT_ARGB8888;
		    tex.width = tmp.width;
		    tex.height = tmp.height;
		    tex.pitch = MAX2(tmp.width / 16, 1);
		    do {
			    ret = drmCommandWriteRead(bo->bom->fd,
						      DRM_RADEON_TEXTURE,
						      &tex,
						      sizeof(drm_radeon_texture_t));
			    if (ret) {
				    if (RADEON_DEBUG & RADEON_IOCTL)
					    fprintf(stderr, "DRM_RADEON_TEXTURE:  again!\n");
				    usleep(1);
			    }
		    } while (ret == -EAGAIN);
	    }
	    bo_legacy->dirty = 0;
	    bo_legacy->tobj->base.dirty_images[0] = 0;
    }
    return 0;
}

/* 
 *  radeon_bo_legacy_validate -
 *  returns:
 *  0 - all good
 *  -EINVAL - mapped buffer can't be validated
 *  -EAGAIN - restart validation we've kicked all the buffers out
 */
int radeon_bo_legacy_validate(struct radeon_bo *bo,
                              uint32_t *soffset,
                              uint32_t *eoffset)
{
    struct radeon_bo_int *boi = (struct radeon_bo_int *)bo;
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)boi->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;
    int r;
    int retries = 0;

    if (bo_legacy->map_count) {
        fprintf(stderr, "bo(%p, %d) is mapped (%d) can't valide it.\n",
                bo, boi->size, bo_legacy->map_count);
        return -EINVAL;
    }
    if(boi->size == 0) {
        fprintf(stderr, "bo(%p) has size 0.\n", bo);
        return -EINVAL;
    }
    if (bo_legacy->static_bo || bo_legacy->validated) {
        *soffset = bo_legacy->offset;
        *eoffset = bo_legacy->offset + boi->size;

        return 0;
    }
    if (!(boi->domains & RADEON_GEM_DOMAIN_GTT)) {

        r = bo_vram_validate(boi, soffset, eoffset);
        if (r) {
	    legacy_track_pending(&boml->base, 0);
	    legacy_kick_all_buffers(boml);
	    retries++;
	    if (retries == 2) {
		fprintf(stderr,"legacy bo: failed to get relocations into aperture\n");
		assert(0);
		exit(-1);
	    }
	    return -EAGAIN;
        }
    }
    *soffset = bo_legacy->offset;
    *eoffset = bo_legacy->offset + boi->size;
    bo_legacy->validated = 1;

    return 0;
}

void radeon_bo_legacy_pending(struct radeon_bo *bo, uint32_t pending)
{
    struct radeon_bo_int *boi = (struct radeon_bo_int *)bo;
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)boi->bom;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;

    bo_legacy->pending = pending;
    bo_legacy->is_pending++;
    /* add to pending list */
    radeon_bo_ref(bo);
    if (bo_legacy->is_pending > 1) {
        return;    
    }
    bo_legacy->pprev = boml->pending_bos.pprev;
    bo_legacy->pnext = NULL;
    bo_legacy->pprev->pnext = bo_legacy;
    boml->pending_bos.pprev = bo_legacy;
    boml->cpendings++;
}

void radeon_bo_manager_legacy_dtor(struct radeon_bo_manager *bom)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bom;
    struct bo_legacy *bo_legacy;

    if (bom == NULL) {
        return;
    }
    bo_legacy = boml->bos.next;
    while (bo_legacy) {
        struct bo_legacy *next;

        next = bo_legacy->next;
        bo_free(bo_legacy);
        bo_legacy = next;
    }
    driDestroyTextureHeap(boml->texture_heap);
    free(boml->free_handles);
    free(boml);
}

static struct bo_legacy *radeon_legacy_bo_alloc_static(struct bo_manager_legacy *bom,
						       int size,
						       uint32_t offset)
{
    struct bo_legacy *bo;

    bo = bo_allocate(bom, size, 0, RADEON_GEM_DOMAIN_VRAM, 0);

    if (bo == NULL)
	return NULL;
    bo->static_bo = 1;
    bo->offset = offset + bom->fb_location;
    bo->base.handle = bo->offset;
    bo->ptr = bom->screen->driScreen->pFB + offset;
    if (bo->base.handle > bom->nhandle) {
        bom->nhandle = bo->base.handle + 1;
    }
    radeon_bo_ref((struct radeon_bo *)&(bo->base));
    return bo;
}

struct radeon_bo_manager *radeon_bo_manager_legacy_ctor(struct radeon_screen *scrn)
{
    struct bo_manager_legacy *bom;
    struct bo_legacy *bo;
    unsigned size;

    bom = (struct bo_manager_legacy*)
          calloc(1, sizeof(struct bo_manager_legacy));
    if (bom == NULL) {
        return NULL;
    }

    make_empty_list(&bom->texture_swapped);

    bom->texture_heap = driCreateTextureHeap(0,
                                             bom,
                                             scrn->texSize[0],
                                             12,
                                             RADEON_NR_TEX_REGIONS,
                                             (drmTextureRegionPtr)scrn->sarea->tex_list[0],
                                             &scrn->sarea->tex_age[0],
                                             &bom->texture_swapped,
                                             sizeof(struct bo_legacy_texture_object),
                                             &bo_legacy_tobj_destroy);
    bom->texture_offset = scrn->texOffset[0];

    bom->base.funcs = &bo_legacy_funcs;
    bom->base.fd = scrn->driScreen->fd;
    bom->bos.next = NULL;
    bom->bos.prev = NULL;
    bom->pending_bos.pprev = &bom->pending_bos;
    bom->pending_bos.pnext = NULL;
    bom->screen = scrn;
    bom->fb_location = scrn->fbLocation;
    bom->nhandle = 1;
    bom->cfree_handles = 0;
    bom->nfree_handles = 0x400;
    bom->free_handles = (uint32_t*)malloc(bom->nfree_handles * 4);
    if (bom->free_handles == NULL) {
        radeon_bo_manager_legacy_dtor((struct radeon_bo_manager*)bom);
        return NULL;
    }

    /* biggest framebuffer size */
    size = 4096*4096*4; 

    /* allocate front */
    bo = radeon_legacy_bo_alloc_static(bom, size, bom->screen->frontOffset);

    if (!bo) {
        radeon_bo_manager_legacy_dtor((struct radeon_bo_manager*)bom);
        return NULL;
    }
    if (scrn->sarea->tiling_enabled) {
        bo->base.flags = RADEON_BO_FLAGS_MACRO_TILE;
    }

    /* allocate back */
    bo = radeon_legacy_bo_alloc_static(bom, size, bom->screen->backOffset);

    if (!bo) {
        radeon_bo_manager_legacy_dtor((struct radeon_bo_manager*)bom);
        return NULL;
    }
    if (scrn->sarea->tiling_enabled) {
        bo->base.flags = RADEON_BO_FLAGS_MACRO_TILE;
    }

    /* allocate depth */
    bo = radeon_legacy_bo_alloc_static(bom, size, bom->screen->depthOffset);

    if (!bo) {
        radeon_bo_manager_legacy_dtor((struct radeon_bo_manager*)bom);
        return NULL;
    }
    bo->base.flags = 0;
    if (scrn->sarea->tiling_enabled) {
        bo->base.flags |= RADEON_BO_FLAGS_MACRO_TILE;
        bo->base.flags |= RADEON_BO_FLAGS_MICRO_TILE;
    }
    return (struct radeon_bo_manager*)bom;
}

void radeon_bo_legacy_texture_age(struct radeon_bo_manager *bom)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bom;
    DRI_AGE_TEXTURES(boml->texture_heap);
}

unsigned radeon_bo_legacy_relocs_size(struct radeon_bo *bo)
{
    struct radeon_bo_int *boi = (struct radeon_bo_int *)bo;
    struct bo_legacy *bo_legacy = (struct bo_legacy*)bo;

    if (bo_legacy->static_bo || (boi->domains & RADEON_GEM_DOMAIN_GTT)) {
        return 0;
    }
    return boi->size;
}

/*
 * Fake up a bo for things like texture image_override.
 * bo->offset already includes fb_location
 */
struct radeon_bo *radeon_legacy_bo_alloc_fake(struct radeon_bo_manager *bom,
					      int size,
	                                      uint32_t offset)
{
    struct bo_manager_legacy *boml = (struct bo_manager_legacy *)bom;
    struct bo_legacy *bo;

    bo = bo_allocate(boml, size, 0, RADEON_GEM_DOMAIN_VRAM, 0);

    if (bo == NULL)
	return NULL;
    bo->static_bo = 1;
    bo->offset = offset;
    bo->base.handle = bo->offset;
    bo->ptr = boml->screen->driScreen->pFB + (offset - boml->fb_location);
    if (bo->base.handle > boml->nhandle) {
        boml->nhandle = bo->base.handle + 1;
    }
    radeon_bo_ref((struct radeon_bo *)&(bo->base));
    return (struct radeon_bo *)&(bo->base);
}

