/* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include "dev/drm/drmP.h"
#include "dev/drm/drm.h"
#include "dev/drm/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
 * WITTAWAT YAMWONG, 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;
}

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;
}
