/*
 *  Driver for ZA2
 *  Copyright (c) by Martin Pahl <pahl@tnt.uni-hannover.de>
 *
 *
 *   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.
 *
 */

#define SND_MAIN_OBJECT_FILE
#include "driver.h"
#include "za2.h"
#include "initval.h"

#include "za2-lowlevel.c"
#include "za2-driver.c"

int snd_index[SND_CARDS] = SND_DEFAULT_IDX;	/* Index 1-MAX */
char *snd_id[SND_CARDS] = SND_DEFAULT_STR;	/* ID for this card */
int snd_port[SND_CARDS] = {0x210,-1};	/* PnP setup */
int snd_irq[SND_CARDS] = SND_DEFAULT_IRQ;	/* 10,11,5,7,9,11,12,15 */
int snd_mpu_irq[SND_CARDS] = SND_DEFAULT_IRQ;	/* 9,11,12,15 */
int snd_dma1[SND_CARDS] = SND_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
int snd_dma1_size[SND_CARDS] = SND_DEFAULT_DMA_SIZE;	/* 8,16,32,64,128 */
int snd_dma2[SND_CARDS] = SND_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
int snd_dma2_size[SND_CARDS] = SND_DEFAULT_DMA_SIZE;	/* 8,16,32,64,128 */
#ifdef MODULE_PARM
MODULE_PARM(snd_index, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_index, "Index value for ZA2 soundcard.");
MODULE_PARM(snd_id, "1-" __MODULE_STRING(SND_CARDS) "s");
MODULE_PARM_DESC(snd_id, "ID string for ZA2 soundcard.");
MODULE_PARM(snd_port, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_port, "Port # for ZA2 driver.");
MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_irq, "IRQ # for ZA2 driver.");
MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_dma1, "DMA1 # for ZA2 driver.");
MODULE_PARM(snd_dma1_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_dma1_size, "Size of DMA1 # for ZA2 driver.");
MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_dma2, "DMA2 # for ZA2 driver.");
MODULE_PARM(snd_dma2_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_dma2_size, "Size of DMA2 # for ZA2 driver.");
#endif

static struct snd_card_za2 {
	snd_irq_t *irqptr;
	snd_dma_t *dma1ptr;
	snd_dma_t *dma2ptr;
	snd_card_t *card;
	snd_pcm_t *pcm;
	za2_t *codec;
} *snd_card_za2_cards[SND_CARDS] = SND_DEFAULT_PTR;

static void snd_card_za2_use_inc(snd_card_t * card)
{
	MOD_INC_USE_COUNT;
}

static void snd_card_za2_use_dec(snd_card_t * card)
{
	MOD_DEC_USE_COUNT;
}

static snd_pcm_t *snd_card_za2_detect(snd_card_t * card,
				      unsigned short port,
				      snd_irq_t *irqptr,
				      snd_dma_t *dma1ptr,
				      snd_dma_t *dma2ptr)
{
	snd_pcm_t *pcm;

	printk("ZA2: snd_card_za2_detect: port=0x%x\n",port);
	if (snd_register_ioport(card, port, 8, "ZA2",NULL) < 0)
		return NULL;
	pcm = snd_za2_new_device(card, port, irqptr, dma1ptr, dma2ptr, 
				 0, ZA2_BIT_MODE0);
	if (!pcm) {
		snd_unregister_ioports(card);
		return NULL;
	}
	return pcm;
}

static void snd_card_za2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	struct snd_card_za2 *acard;
#if 0   
	register unsigned char status;
#endif   

#if 0
	printk("ZA2: card_za2_interrupt\n");
#endif
	acard = (struct snd_card_za2 *) dev_id;
	if (acard == NULL || acard->pcm == NULL)
		return;
	snd_za2_interrupt(acard->pcm);
}


static int snd_card_za2_resources(int dev, struct snd_card_za2 *acard,
				  snd_card_t * card)
{
	static int possible_irqs[] = {10,11,12,-1};
	static int possible_dmas[] = {5,6,7,-1};
	int err;
	
	if ((err = snd_register_interrupt(card, "ZA2",
					  snd_irq[dev], 
					  SND_IRQ_TYPE_ISA,
					  snd_card_za2_interrupt, 
					  acard,
					  possible_irqs,
					  &acard->irqptr)) < 0)
		return err;
	
	if ((err = snd_register_dma_channel(card,
					    "ZA2 - DMA1", 
					    snd_dma1[dev],
					    SND_DMA_TYPE_ISA, 
					    snd_dma1_size[dev],
					    possible_dmas,
					    &acard->dma1ptr)) < 0) {
		return err;
	}
	if (snd_dma2[dev] >= 0) {
		if ((err = snd_register_dma_channel(card,
						    "ZA2 - DMA2", 
						    snd_dma2[dev],
						    SND_DMA_TYPE_ISA, 
						    snd_dma2_size[dev],
						    possible_dmas,
						    &acard->dma2ptr)) < 0) {
			return err;
		}
	} 
	else {
		acard->dma2ptr = NULL;
	}
	return 0;
}

static int snd_card_za2_probe(int dev, struct snd_card_za2 *acard)
{
	snd_card_t *card;
	snd_pcm_t *pcm = NULL;
	snd_pcm1_t *pcm1;
	za2_t *codec;
	
	if (snd_port[dev] == SND_AUTO_PORT) {
		snd_printk("probing for ZA2 isn't supported\n");
		snd_printk("port = 0x%x\n", snd_port[dev]);
		return -ENODEV;
	}
	
	card = snd_card_new(snd_index[dev], snd_id[dev],
			    snd_card_za2_use_inc, snd_card_za2_use_dec);
	if (!card)
		return -ENOMEM;
	card->type = SND_CARD_TYPE_ZA2;
	if (snd_card_za2_resources(dev, acard, card) < 0) {
		snd_card_free(card);
		return -EBUSY;
	}
	pcm = snd_card_za2_detect(card, snd_port[dev],
				  acard->irqptr, acard->dma1ptr,
				  acard->dma2ptr == NULL ?
				  acard->dma1ptr : acard->dma2ptr);
	if (!pcm) {
		snd_card_free(card);
		return -ENODEV;
	}
	pcm1 = (snd_pcm1_t *) pcm->private_data;
	codec = (za2_t *) pcm1->private_data;
	acard->codec = codec;
	
	if (snd_pcm_register(pcm, 0)) {
		goto __nodev;
	}
	if (snd_za2_init(pcm,1)<0) {
		snd_pcm_unregister(pcm);
		pcm=NULL;
		goto __nodev;
	}
#ifdef OLD
	printk("ZA2: probe: enable_irq %d\n",acard->irqptr->irq);
	snd_enable_irq(card, acard->irqptr);
#endif
	strcpy(card->abbreviation, "ZA2");
	strcpy(card->shortname, pcm->name);
	sprintf(card->longname, "%s at 0x%x, irq %i, dma %i",
		pcm->name,
		codec->port,
		acard->irqptr->irq,
		acard->dma1ptr->dma);
	if (acard->dma2ptr != NULL) {
		sprintf(card->longname + strlen(card->longname), "&%i",
			acard->dma2ptr->dma);
	}
	if (!snd_card_register(card)) {
		acard->card = card;
		acard->pcm = pcm;
		return 0;
	}
	snd_pcm_unregister(pcm);
	snd_card_free(card);
	return -ENOMEM;
	
 __nodev:
	if (pcm)
		snd_pcm_free(pcm);
	snd_card_free(card);
	return -ENXIO;
}

int init_module(void)
{
	int dev, cards;
	struct snd_card_za2 *acard;

#ifndef LINUX_2_1
	register_symtab(NULL);
#endif
	for (dev = cards = 0; dev < SND_CARDS && snd_port[dev] > 0; dev++) {
		acard = (struct snd_card_za2 *)
				snd_malloc(sizeof(struct snd_card_za2));
		if (!acard)
			continue;
		memset(acard, 0, sizeof(struct snd_card_za2));
		if (snd_card_za2_probe(dev, acard) < 0) {
			snd_free(acard, sizeof(struct snd_card_za2));
			if (snd_port[dev] == SND_AUTO_PORT)
				break;
			snd_printk("ZA2 soundcard #%i not found at 0x%x or device busy\n", dev + 1, snd_port[dev]);
			continue;
		}
		snd_card_za2_cards[dev] = acard;
		cards++;
	}
	if (!cards) {
		snd_printk("ZA2 soundcard #%i not found or device busy\n", dev + 1);
		return -ENODEV;
	}
	printk("ZA2: found %d cards\n",cards);
	return 0;
}

void cleanup_module(void)
{
	int idx;
	struct snd_card_za2 *acard;

	for (idx = 0; idx < SND_CARDS; idx++) {
		acard = snd_card_za2_cards[idx];
		if (acard) {
			snd_card_unregister(acard->card);
			if (acard->pcm) {
				snd_pcm_unregister(acard->pcm);
			}
			snd_card_free(acard->card);
			snd_free(acard, sizeof(struct snd_card_za2));
		}
	}
}

