/* 
    ALSA memory allocation module for the RME Digi9652
  
 	Copyright(c) 1999 IEM - Winfried Ritsch
        Copyright (C) 1999 Paul Barton-Davis 

    This module is only needed if you compiled the rme9652 driver with
    the PREALLOCATE_MEMORY option. It allocates the memory need to
    run the board and holds it until the module is unloaded. Because
    we need 2 contiguous 1.6MB regions for the board, it can be
    a problem getting them once the system memory has become fairly
    fragmented. 

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    rme9652_mem.c,v 1.3.2.1 2000/05/24 10:41:21 iwai Exp
*/

#define SND_MAIN_OBJECT_FILE

#include "../../include/driver.h"
#include "../../include/rme9652.h"

/* export */

int snd_card_cnt = 1;
MODULE_PARM(snd_card_cnt, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_card_cnt, "Number of cards to allocate buffers for.");

MODULE_AUTHOR("Winfried Ritsch, Paul Barton-Davis <pbd@op.net>");
MODULE_DESCRIPTION("Memory allocator for RME Hammerfall");

/* Since we don't know at this point if we're allocating memory for a
   Hammerfall or a Hammerfall/Light, assume the worst and allocate
   space for the maximum number of channels.
		   
   See note in rme9652.h about why we allocate for an extra channel.  
*/

#define TOTAL_SIZE (26+1)*(RME9652_CHANNEL_BUFFER_BYTES)
#define NBUFS   2*SND_CARDS

#define RME9652_BUF_ALLOCATED 0x1
#define RME9652_BUF_USED      0x2

struct {
    void *buf;
    char  flags;
} rme9652_buffers[NBUFS];

/* These are here so that we have absolutely no dependencies on any
   other modules. Dependencies can (1) cause us to lose in the rush
   for 2x 1.6MB chunks of contiguous memory and (2) make driver
   debugging difficult because unloading and reloading the snd module
   causes us to have to do the same for this one. Since on 2.2
   kernels, and before, we can rarely if ever allocate memory after
   starting things running, this would be bad.
*/

static char *rme9652_malloc_pages(unsigned long size, int *res_pg, int dma)
{
	int pg;
	char *buf;

	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
	buf = (char *) __get_free_pages(GFP_KERNEL | (dma ? GFP_DMA : 0), pg);
	if (buf) {
		int page, last_page;

		page = MAP_NR(buf);
		last_page = page + (1 << pg);
		while (page < last_page)
			set_bit(PG_reserved, &mem_map[page++].flags);
	}
	*res_pg = pg;
	return buf;
}


static void rme9652_free_pages(char *ptr, unsigned long size)
{
	int pg, page, last_page;

	if (ptr == NULL)
		return;
	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
	page = MAP_NR(ptr);
	last_page = page + (1 << pg);
	while (page < last_page)
		clear_bit(PG_reserved, &mem_map[page++].flags);
	free_pages((unsigned long) ptr, pg);
}

void *snd_rme9652_get_buffer (void)

{
	int i;

	for (i = 0; i < NBUFS; i++) {
		if (rme9652_buffers[i].flags == RME9652_BUF_ALLOCATED) {
			rme9652_buffers[i].flags |= RME9652_BUF_USED;
			MOD_INC_USE_COUNT;
			return rme9652_buffers[i].buf;
		}
	}

	return 0;
}

void snd_rme9652_free_buffer (void *addr)

{
	int i;

	for (i = 0; i < NBUFS; i++) {
		if (rme9652_buffers[i].buf == addr) {
			MOD_DEC_USE_COUNT;
			rme9652_buffers[i].flags &= ~RME9652_BUF_USED;
			return;
		}
	}

	snd_printk ("RME Hammerfall: unknown buffer address "
		    "passed to free buffer");
}

static void __exit rme9652_free_buffers (void)

{
	int i;

	for (i = 0; i < NBUFS; i++) {

		/* We rely on general module code to prevent
		   us from being unloaded with buffers in use.

		   However, not quite. Do not release memory
		   if it is still marked as in use. This might
		   be unnecessary.
		*/

		if (rme9652_buffers[i].flags == RME9652_BUF_ALLOCATED) {
			rme9652_free_pages (rme9652_buffers[i].buf, TOTAL_SIZE);
			rme9652_buffers[i].flags = 0;
		}
	}
}				 

static int __init alsa_rme9652_mem_init(void)
{
  
	int pg;
	int i;

	/* make sure our buffer records are clean */

	for (i = 0; i < NBUFS; i++) {
		rme9652_buffers[i].buf = 0;
		rme9652_buffers[i].flags = 0;
	}

	/* ensure sane values for the number of buffers */

	if (snd_card_cnt > SND_CARDS) {
		snd_card_cnt = SND_CARDS;
	}

	if (snd_card_cnt == 0) {
		snd_card_cnt = 1;
	}

	/* Remember: 2 buffers per card, one for capture, one for
	   playback.
	*/
	
	for (i = 0; i < snd_card_cnt * 2; i++) {

		rme9652_buffers[i].buf = 
			rme9652_malloc_pages (TOTAL_SIZE, &pg, 1);

		if (rme9652_buffers[i].buf == 0) {
			rme9652_free_buffers ();

			snd_printk ("RME Hammerfall: "
				    "no memory available for buffer %d\n", i);
			return -ENOMEM;
		}

		rme9652_buffers[i].flags = RME9652_BUF_ALLOCATED;
	}
	
	return 0;
}

static void __exit alsa_rme9652_mem_exit(void)
{
	rme9652_free_buffers ();
}

module_init(alsa_rme9652_mem_init)
module_exit(alsa_rme9652_mem_exit)

EXPORT_SYMBOL(snd_rme9652_get_buffer);
EXPORT_SYMBOL(snd_rme9652_free_buffer);
