/*
 *  Driver for Trident 4DWave DX/NX AudioPCI soundcard
 *  audio@tridentmicro.com
 *  Fri Feb 19 15:55:28 MST 1999
 *
 *
 *   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 "trid4dwave.h"
#include "initval.h"

#ifdef __QNXNTO__
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_dac_frame_size_dx[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 64};
int snd_adc_frame_size_dx[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 64};
int snd_dac_frame_size_nx[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_adc_frame_size_nx[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 64};
#else
int snd_index[SND_CARDS] = {-1,-1,-1,-1,-1,-1,-1,-1};	/* Index 1-MAX */
char *snd_id[SND_CARDS];	/* ID for this card */
int snd_dac_frame_size_dx[SND_CARDS] = {64,64,64,64,64,64,64,64};
int snd_adc_frame_size_dx[SND_CARDS] = {64,64,64,64,64,64,64,64};
int snd_dac_frame_size_nx[SND_CARDS] = {128,128,128,128,128,128,128,128};
int snd_adc_frame_size_nx[SND_CARDS] = {64,64,64,64,64,64,64,64};
#endif

struct snd_trident
{
	struct snd_pci_dev pci;
	snd_irq_t *irqptr;
	snd_dma_t *dma1ptr;					/* DAC frame */
	snd_dma_t *dma2ptr;					/* ADC frame */
	snd_card_t *card;
	trident_t *trident;
	snd_pcm_t *pcm;						/* ADC/DAC */
	snd_kmixer_t *mixer;
};

#ifdef __QNXNTO__
static struct snd_trident *snd_trident_cards[SND_CARDS]= SND_DEFAULT_PTR;

#else
static struct snd_trident *snd_trident_cards[SND_CARDS];

#endif

/*
 * static int snd_trident_detect(snd_card_t * card, struct snd_trident *scard, int pci_index)
 * {
 * snd_printk("cards: called snd_trident_detect\n");
 * if (!snd_pci_find_device(PCI_VENDOR_ID_TRIDENT,
 * PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
 * pci_index, &scard->pci)) {
 * card->type = SND_CARD_TYPE_TRID4DWAVEDX;
 * } else if (!snd_pci_find_device(PCI_VENDOR_ID_TRIDENT,
 * PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
 * pci_index, &scard->pci)) {
 * card->type = SND_CARD_TYPE_TRID4DWAVENX;
 * } else {
 * return -ENODEV;
 * }
 * return 0;
 * }
 */
static void 
snd_trid_interrupt (void *dev_id)
{
	struct snd_trident *scard= (struct snd_trident *) dev_id;

	if (!scard || !scard->trident)
		return;
	snd_trident_interrupt (scard->trident);
}

static int 
snd_trident_resources (snd_card_t * card,
	struct snd_trident *scard, int dev)
{
	int     err;
	int     dacSize;
	int     adcSize;

	snd_printk ("called snd_trident_resources\n");

	if (card->type == SND_CARD_TYPE_TRID4DWAVEDX)
	{
		dacSize= snd_dac_frame_size_dx[dev];
		adcSize= snd_adc_frame_size_dx[dev];
	}
	else
	{
		dacSize= snd_dac_frame_size_nx[dev];
		adcSize= snd_adc_frame_size_nx[dev];
	}

	if ((err= snd_register_ioport (card,
				PCI_IO_ADDR (scard->pci.base_address[0]),
				0x100, "Trident 4DWave PCI", NULL)) < 0)
		return err;

	if ((err= snd_register_interrupt (card,
				"Trident 4DWave PCI", scard->pci.irq,
				SND_IRQ_TYPE_PCI, snd_trid_interrupt,
				scard, NULL, &scard->irqptr)) < 0)
		return err;

	if ((err= snd_register_dma_channel (card,
				"Trident 4DWave PCI - DAC frame", 0,
				SND_DMA_TYPE_PCI, dacSize,
				NULL, &scard->dma1ptr)) < 0)
		return err;
	snd_printk ("registered dac dma buffer of size %d Kilobytes\n", dacSize);

	if ((err= snd_register_dma_channel (card,
				"Trident 4DWave PCI - ADC frame", 1,
				SND_DMA_TYPE_PCI, adcSize,
				NULL, &scard->dma2ptr)) < 0)
		return err;
	snd_printk ("registered adc dma buffer of size %d Kilobytes\n", adcSize);

	return 0;
}

static int 
snd_trident_probe (int dev, struct snd_trident *scard, int device, int pci_index)
{
	snd_card_t *card;
	snd_pcm_t *pcm= NULL;
	snd_kmixer_t *mixer= NULL;

	snd_printk ("cards: called snd_trident_probe\n");
	snd_printk ("cards: dev=%d\n", dev);

	card= snd_card_new (snd_index[dev], snd_id[dev]);
	snd_printk ("cards: called snd_card_new\n");
	if (!card)
		return -ENOMEM;

	card->static_data= scard;
	if (snd_trident_detect (card, &scard->pci, pci_index, device) < 0)
	{
		snd_card_free (card);
		return -ENODEV;
	}

	if (snd_trident_resources (card, scard, dev) < 0)
	{
		snd_card_free (card);
		return -ENODEV;
	}
	scard->trident= snd_trident_create (card, &scard->pci,
		scard->dma1ptr, scard->dma2ptr,
		scard->irqptr);
	if (!scard->trident)
		goto __nodev;
	snd_printk ("cards: calling snd_trident_mixer\n");
	mixer= snd_trident_mixer (scard->trident);
	if (!mixer)
		goto __nodev;
	snd_printk ("cards: calling snd_trident_pcm\n");
	pcm= snd_trident_pcm (scard->trident);
	if (!pcm)
		goto __nodev;
	snd_printk ("cards: calling snd_mixer_register\n");
	if (snd_mixer_register (mixer, 0) < 0)
		goto __nodev;
	snd_printk ("cards: calling snd_pcm_register\n");
	if (snd_pcm_register (pcm, 0) < 0)
	{
		snd_mixer_unregister (mixer);
		mixer= NULL;
		goto __nodev;
	}

	snd_enable_irq (scard->irqptr);

	strcpy (card->abbreviation, "4DWave");
	strcpy (card->shortname, "Trident 4DWave PCI");
	sprintf (card->longname, "%s %s at 0x%lx, irq %i",
		card->shortname,
		card->type == SND_CARD_TYPE_TRID4DWAVEDX ? "4DWAVEDX" : "4DWAVENX",
		PCI_IO_ADDR (scard->pci.base_address[0]),
		scard->irqptr->irq);

	snd_printk ("cards: calling snd_card_register\n");
	if (!snd_card_register (card))
	{
		scard->card= card;
		scard->mixer= mixer;
		scard->pcm= pcm;
		return 0;
	}
	goto __nodev;

  __nodev:
	if (mixer)
		snd_mixer_free (mixer);
	if (pcm)
		snd_pcm_free (pcm);
	if (scard->trident)
	{
		snd_trident_free (scard->trident);
		scard->trident= NULL;
	}
	snd_card_free (card);
	return -ENXIO;
}

static int 
snd_trid_free (int dev)
{
	struct snd_trident *scard;
	snd_pcm_t *pcm;

	scard= snd_trident_cards[dev];
	snd_trident_cards[dev]= NULL;
	if (scard)
	{
		snd_card_unregister (scard->card);
		if (scard->mixer)
		{
			snd_mixer_unregister (scard->mixer);
		}
		if (scard->pcm)
		{
			pcm= scard->pcm;
			scard->pcm= NULL;							   /* turn off interrupts */
			snd_pcm_unregister (pcm);
		}
		if (scard->trident)
		{
			snd_trident_free (scard->trident);
			scard->trident= NULL;
		}
		snd_card_free (scard->card);
		snd_free (scard, sizeof (struct snd_trident));
	}
	return 0;
}

int 
snd_trid_init (void)
{
	int     dev, cards= 0;
	struct snd_trident *scard= NULL;

	for (dev= 0; dev < SND_CARDS; dev++)
	{
		if (!scard)
		{
			scard= (struct snd_trident *) snd_malloc (sizeof (struct snd_trident));

			if (!scard)
				continue;
			memset (scard, 0, sizeof (struct snd_trident));
		}
		if (snd_trident_probe (cards, scard, SND_CARD_TYPE_TRID4DWAVEDX, dev) == 0)
		{
			snd_trident_cards[cards]= scard;
			snd_printd ("Found 4DwaveDX #%d\n", cards);
			cards++;
			scard= NULL;
			if (cards > SND_CARDS)
				return 0;
		}
		else
			break;
	}
	for (dev= 0; dev < SND_CARDS; dev++)
	{
		if (!scard)
		{
			scard= (struct snd_trident *) snd_malloc (sizeof (struct snd_trident));

			if (!scard)
				continue;
			memset (scard, 0, sizeof (struct snd_trident));
		}
		if (snd_trident_probe (cards, scard, SND_CARD_TYPE_TRID4DWAVENX, dev) == 0)
		{
			snd_trident_cards[cards]= scard;
			snd_printd ("Found 4DwaveNX #%d\n", cards);
			cards++;
			scard= NULL;
			if (cards > SND_CARDS)
				return 0;
		}
		else
			break;
	}
	snd_free (scard, sizeof (struct snd_trident));


	if (!cards)
	{
		snd_printk ("Trident 4DWave PCI soundcard #%i not found or device busy\n", dev + 1);
		return -ENODEV;
	}
	return 0;
}

void 
snd_trid_cleanup (void)
{
	int     dev;

	for (dev= 0; dev < SND_CARDS; dev++)
		snd_trid_free (dev);
}
