/* sis_ds.c -- Private header for Direct Rendering Manager -*- linux-c -*-
 * Created: Mon Jan  4 10:05:05 1999 by sclin@sis.com.tw
 *
 * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
 * 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, sublicense,
 * 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 above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 * 
 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * PRECISION INSIGHT 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.
 * 
 * Authors:
 *    Sung-Ching Lin <sclin@sis.com.tw>
 * 
 */

#include "sis.h"
#include "drmP.h"
#include "drm.h"
#include "sis_ds.h"

/* Set Data Structure, not check repeated value
 * temporarily used
 */

set_t *setInit(void)
{
	int i;
	set_t *set;

	set = (set_t *)DRM(alloc)(sizeof(set_t), DRM_MEM_DRIVER);
	if (set != NULL) {
		for (i = 0; i < SET_SIZE; i++) {
			set->list[i].free_next = i + 1;    
			set->list[i].alloc_next = -1;
		}
		set->list[SET_SIZE-1].free_next = -1;
		set->free = 0;
		set->alloc = -1;
		set->trace = -1;
	}
	return set;
}

int setAdd(set_t *set, ITEM_TYPE item)
{
	int free = set->free;
  
	if (free != -1) {
		set->list[free].val = item;
		set->free = set->list[free].free_next;
	} else {
		return 0;
	}

	set->list[free].alloc_next = set->alloc;
	set->alloc = free;  
	set->list[free].free_next = -1;    

	return 1;
}

int setDel(set_t *set, ITEM_TYPE item)
{
	int alloc = set->alloc;
	int prev = -1;  

	while (alloc != -1) {
		if (set->list[alloc].val == item) {
			if (prev != -1)
				set->list[prev].alloc_next =
				    set->list[alloc].alloc_next;
			else
				set->alloc = set->list[alloc].alloc_next;
			break;
		}
		prev = alloc;
		alloc = set->list[alloc].alloc_next;
	}

	if (alloc == -1)
		return 0;

	set->list[alloc].free_next = set->free;
	set->free = alloc;
	set->list[alloc].alloc_next = -1;

	return 1;
}

/* setFirst -> setAdd -> setNext is wrong */

int setFirst(set_t *set, ITEM_TYPE *item)
{
	if (set->alloc == -1)
		return 0;

	*item = set->list[set->alloc].val;
	set->trace = set->list[set->alloc].alloc_next;

	return 1;
}

int setNext(set_t *set, ITEM_TYPE *item)
{
	if (set->trace == -1)
		return 0;

	*item = set->list[set->trace].val;
	set->trace = set->list[set->trace].alloc_next;

	return 1;
}

int setDestroy(set_t *set)
{
	DRM(free)(set, sizeof(set_t), DRM_MEM_DRIVER);

	return 1;
}

/*
 * GLX Hardware Device Driver common code
 * Copyright (C) 1999 Wittawat Yamwong
 *
 * 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, sublicense,
 * 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 above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * KEITH WHITWELL, OR ANY OTHER CONTRIBUTORS 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.
 *
 */

#define ISFREE(bptr) ((bptr)->free)

memHeap_t *mmInit(int ofs,
		  int size)
{
	PMemBlock blocks;

	if (size <= 0)
		return NULL;

	blocks = (TMemBlock *)DRM(calloc)(1, sizeof(TMemBlock), DRM_MEM_DRIVER);
	if (blocks != NULL) {
		blocks->ofs = ofs;
		blocks->size = size;
		blocks->free = 1;
		return (memHeap_t *)blocks;
	} else
		return NULL;
}

/* Checks if a pointer 'b' is part of the heap 'heap' */
int mmBlockInHeap(memHeap_t *heap, PMemBlock b)
{
	TMemBlock *p;

	if (heap == NULL || b == NULL)
		return 0;

	p = heap;
	while (p != NULL && p != b) {
		p = p->next;
	}
	if (p == b)
		return 1;
	else
		return 0;
}

/* Kludgey workaround for existing i810 server.  Remove soon.
 */
memHeap_t *mmAddRange( memHeap_t *heap,
		       int ofs,
		       int size )
{
	PMemBlock blocks;
	blocks = (TMemBlock *)DRM(calloc)(2, sizeof(TMemBlock), DRM_MEM_DRIVER);
	if (blocks != NULL) {
		blocks[0].size = size;
		blocks[0].free = 1;
		blocks[0].ofs = ofs;
		blocks[0].next = &blocks[1];

		/* Discontinuity - stops JoinBlock from trying to join
		 * non-adjacent ranges.
		 */
		blocks[1].size = 0;
		blocks[1].free = 0;
		blocks[1].ofs = ofs+size;
		blocks[1].next = (PMemBlock)heap;
		return (memHeap_t *)blocks;
	} else
		return heap;
}

static TMemBlock* SliceBlock(TMemBlock *p, 
			     int startofs, int size, 
			     int reserved, int alignment)
{
	TMemBlock *newblock;

	/* break left */
	if (startofs > p->ofs) {
		newblock = (TMemBlock*) DRM(calloc)(1, sizeof(TMemBlock),
		    DRM_MEM_DRIVER);
		newblock->ofs = startofs;
		newblock->size = p->size - (startofs - p->ofs);
		newblock->free = 1;
		newblock->next = p->next;
		p->size -= newblock->size;
		p->next = newblock;
		p = newblock;
	}

	/* break right */
	if (size < p->size) {
		newblock = (TMemBlock*) DRM(calloc)(1, sizeof(TMemBlock),
		    DRM_MEM_DRIVER);
		newblock->ofs = startofs + size;
		newblock->size = p->size - size;
		newblock->free = 1;
		newblock->next = p->next;
		p->size = size;
		p->next = newblock;
	}

	/* p = middle block */
	p->align = alignment;
	p->free = 0;
	p->reserved = reserved;
	return p;
}

PMemBlock mmAllocMem( memHeap_t *heap, int size, int align2, int startSearch)
{
	int mask,startofs, endofs;
	TMemBlock *p;
	
	if (heap == NULL || align2 < 0 || size <= 0)
		return NULL;

	mask = (1 << align2)-1;
	startofs = 0;
	p = (TMemBlock *)heap;
	while (p != NULL) {
		if (ISFREE(p)) {
			startofs = (p->ofs + mask) & ~mask;
			if ( startofs < startSearch ) {
				startofs = startSearch;
			}
			endofs = startofs+size;
			if (endofs <= (p->ofs+p->size))
				break;
		}
		p = p->next;
	}
	if (p == NULL)
		return NULL;
	p = SliceBlock(p,startofs,size,0,mask+1);
	p->heap = heap;
	return p;
}

static __inline__ int Join2Blocks(TMemBlock *p)
{
	if (p->free && p->next && p->next->free) {
		TMemBlock *q = p->next;
		p->size += q->size;
		p->next = q->next;
		DRM(free)(q, sizeof(TMemBlock), DRM_MEM_DRIVER);
		return 1;
	}
	return 0;
}

int mmFreeMem(PMemBlock b)
{
	TMemBlock *p, *prev;

	if (b == NULL)
		return 0;
	if (b->heap == NULL)
		return -1;

	p = b->heap;
	prev = NULL;
	while (p != NULL && p != b) {
		prev = p;
		p = p->next;
	}
	if (p == NULL || p->free || p->reserved)
		return -1;

	p->free = 1;
	Join2Blocks(p);
	if (prev)
	Join2Blocks(prev);
	return 0;
}

int mmReserveMem(memHeap_t *heap, int offset,int size)
{
	int endofs;
	TMemBlock *p;

	if (heap == NULL || size <= 0)
		return -1;

	endofs = offset + size;
	p = (TMemBlock *)heap;
	while (p && p->ofs <= offset) {
		if (ISFREE(p) && endofs <= (p->ofs+p->size)) {
			SliceBlock(p,offset,size,1,1);
			return 0;
		}
		p = p->next;
	}
	return -1;
}

int mmFreeReserved(memHeap_t *heap, int offset)
{
	TMemBlock *p,*prev;

	if (heap == NULL)
		return -1;

	p = (TMemBlock *)heap;
	prev = NULL;
	while (p != NULL && p->ofs != offset) {
		prev = p;
		p = p->next;
	}
	if (p == NULL || !p->reserved)
		return -1;

	p->free = 1;
	p->reserved = 0;
	Join2Blocks(p);
	if (prev != NULL)
		Join2Blocks(prev);
	return 0;
}

void mmDestroy(memHeap_t *heap)
{
	TMemBlock *p,*q;

	if (heap == NULL)
		return;

	p = (TMemBlock *)heap;
	while (p != NULL) {
		q = p->next;
		DRM(free)(p, sizeof(TMemBlock), DRM_MEM_DRIVER);
		p = q;
	}
}
