/*
 *  Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>
 *
 *
 *   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.
 *
 */

#include "driver.h"
#include "sndpci.h"
#include "audiopci.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_dac1_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_dac2_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_adc_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
#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_dac1_frame_size[SND_CARDS] = {128,128,128,128,128,128,128,128};
int snd_dac2_frame_size[SND_CARDS] = {128,128,128,128,128,128,128,128};
int snd_adc_frame_size[SND_CARDS] = {128,128,128,128,128,128,128,128};
#endif


struct snd_audiopci {
	struct snd_pci_dev pci;
	snd_irq_t *irqptr;
	snd_dma_t *dma1ptr;		// DAC1 frame 
	snd_dma_t *dma2ptr;		// DAC2 frame
	snd_dma_t *dma3ptr;		// ADC frame 
	snd_card_t *card;
	ensoniq_t *ensoniq;
	snd_pcm_t *pcm;		// DAC1/ADC 
	snd_pcm_t *pcm2;	// DAC2 
	snd_kmixer_t *mixer;
	snd_rawmidi_t *midi_uart;
};

#ifdef __QNXNTO__
static struct snd_audiopci *snd_audiopci_cards[SND_CARDS] = SND_DEFAULT_PTR;
#else
static struct snd_audiopci *snd_audiopci_cards[SND_CARDS];
#endif

static void snd_audiopci_interrupt(void *dev_id)
{
	struct snd_audiopci *scard = (struct snd_audiopci *) dev_id;

	if (!scard || !scard->ensoniq)
		return;
	snd_ensoniq_interrupt(scard->ensoniq);
}

static int snd_audiopci_resources(snd_card_t * card, struct snd_audiopci *scard, int dev)
{
	int err;

	if ((err = snd_register_ioport(card, 
			PCI_IO_ADDR(scard->pci.base_address[0]), 
			0x40, "Ensoniq AudioPCI", NULL)) < 0) {
		return err;
	}
	if ((err = snd_register_interrupt(card,
			"Ensoniq AudioPCI", scard->pci.irq,
			SND_IRQ_TYPE_PCI, snd_audiopci_interrupt,
			scard, NULL, &scard->irqptr)) < 0) {
		return err;
	}
	if ((err = snd_register_dma_channel(card,
			"Ensoniq AudioPCI - DAC1 frame", 0,
			SND_DMA_TYPE_PCI, snd_dac1_frame_size[dev],
			NULL, &scard->dma1ptr)) < 0) {
		return err;
	}
	if ((err = snd_register_dma_channel(card,
			"Ensoniq AudioPCI - DAC2 frame", 1,
			SND_DMA_TYPE_PCI, snd_dac2_frame_size[dev],
			NULL, &scard->dma2ptr)) < 0) {
		return err;
	}
	if ((err = snd_register_dma_channel(card,
			"Ensoniq AudioPCI - ADC frame", 2,
			SND_DMA_TYPE_PCI, snd_adc_frame_size[dev],
			NULL, &scard->dma3ptr)) < 0) {
		return err;
	}
	return 0;
}

static int snd_audiopci_probe(int dev, struct snd_audiopci *scard, int device, int pci_index)
{
	snd_card_t *card;
	snd_pcm_t *pcm = NULL, *pcm2 = NULL;
	snd_kmixer_t *mixer = NULL;
	snd_rawmidi_t *midi_uart = NULL;

	card = snd_card_new(snd_index[dev], snd_id[dev]);
	if (!card)
		return -ENOMEM;

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

	if (snd_audiopci_resources(card, scard, dev) < 0) {
		snd_card_free(card);
		return -ENODEV;
	}
	
	scard->ensoniq = snd_ensoniq_create(card, &scard->pci,
				scard->dma1ptr, scard->dma2ptr,
				scard->dma3ptr, scard->irqptr);
	if (!scard->ensoniq)
		goto __nodev;
	mixer = snd_ensoniq_mixer(scard->ensoniq);
	if (!mixer)
		goto __nodev;
	pcm = snd_ensoniq_pcm(scard->ensoniq);
	if (!pcm)
		goto __nodev;
	pcm2 = snd_ensoniq_pcm2(scard->ensoniq);
	if (!pcm2)
		goto __nodev;

	midi_uart = snd_ensoniq_midi(scard->ensoniq);
	if (!midi_uart)
		goto __nodev;
	if (snd_mixer_register(mixer, 0) < 0)
		goto __nodev;
	if (snd_pcm_register(pcm, 0) < 0) {
		snd_mixer_unregister(mixer);
		mixer = NULL;
		goto __nodev;
	}
	if (snd_pcm_register(pcm2, 1) < 0) {
		snd_pcm_unregister(pcm);
		pcm = NULL;
		snd_mixer_unregister(mixer);
		mixer = NULL;
		goto __nodev;
	}
	if (snd_rawmidi_register(midi_uart, 0) < 0) {
		snd_pcm_unregister(pcm2);
		pcm2 = NULL;
		snd_pcm_unregister(pcm);
		pcm = NULL;
		snd_mixer_unregister(mixer);
		mixer = NULL;
		goto __nodev;
	}
	snd_enable_irq(scard->irqptr);

	strcpy(card->abbreviation, "AudioPCI");
	strcpy(card->shortname, "Ensoniq AudioPCI");
	sprintf(card->longname, "%s %s at 0x%lx, irq %i",
		card->shortname,
		card->type == SND_CARD_TYPE_ENS1370 ? "ES1370" : "ES1371",
		PCI_IO_ADDR(scard->pci.base_address[0]),
		scard->irqptr->irq);

	if (!snd_card_register(card)) {
		scard->card = card;
		scard->mixer = mixer;
		scard->pcm = pcm;
		scard->pcm2 = pcm2;
		scard->midi_uart = scard->ensoniq->rmidi = midi_uart;
		return 0;
	}
	goto __nodev;

      __nodev:
	if (midi_uart)
		snd_rawmidi_free(midi_uart);
	if (mixer)
		snd_mixer_free(mixer);
	if (pcm2)
		snd_pcm_free(pcm2);
	if (pcm)
		snd_pcm_free(pcm);
	if (scard->ensoniq) {
		snd_ensoniq_free(scard->ensoniq);
		scard->ensoniq = NULL;
	}
	snd_card_free(card);
	return -ENXIO;
}

static int snd_audiopci_free(int dev)
{
	struct snd_audiopci *scard;
	snd_pcm_t *pcm;

	scard = snd_audiopci_cards[dev];
	snd_audiopci_cards[dev] = NULL;
	if (scard) {
		snd_card_unregister(scard->card);
		if (scard->midi_uart) {
			scard->ensoniq->rmidi = NULL;
			snd_rawmidi_unregister(scard->midi_uart);
		}
		if (scard->mixer)
			snd_mixer_unregister(scard->mixer);
		if (scard->pcm2) {
			pcm = scard->pcm2;
			scard->pcm2 = NULL;	/* turn off interrupts */
			snd_pcm_unregister(pcm);
		}
		if (scard->pcm) {
			pcm = scard->pcm;
			scard->pcm = NULL;	/* turn off interrupts */
			snd_pcm_unregister(pcm);
		}
		if (scard->ensoniq) {
			snd_ensoniq_free(scard->ensoniq);
			scard->ensoniq = NULL;
		}
		snd_card_free(scard->card);
		snd_free(scard, sizeof(struct snd_audiopci));
	}
	return 0;
}

int snd_audiopci_init(void)
{
	int dev, cards = 0;
	struct snd_audiopci *scard = NULL;

	for (dev = 0; dev < SND_CARDS; dev++) {
		if( cards > SND_CARDS) 
			break;
		if (!scard) {
			scard = (struct snd_audiopci *)snd_malloc(sizeof(struct snd_audiopci));
			if (!scard)
				continue;
			memset(scard, 0, sizeof(struct snd_audiopci));
		}
		if (snd_audiopci_probe(cards, scard, SND_CARD_TYPE_ENS1371, dev) == 0) {
			snd_audiopci_cards[cards] = scard;
			snd_printd("Found ENS1371 #%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_audiopci *)snd_malloc(sizeof(struct snd_audiopci));
			if (!scard)
				continue;
			memset(scard, 0, sizeof(struct snd_audiopci));
		}
		if (snd_audiopci_probe(cards, scard, SND_CARD_TYPE_ENS1370, dev) == 0) {
			snd_audiopci_cards[cards] = scard;
			snd_printd("Found ENS1370 #%d\n",cards);
			cards++;
			scard = NULL;
			if( cards > SND_CARDS) 
				return 0;
		} else
			break;
	}
	snd_free(scard, sizeof(struct snd_audiopci));
	if (!cards) {
		snd_printk("Ensoniq AudioPCI soundcard #%i not found or device busy\n", dev + 1);
		return -ENODEV;
	}
	return 0;
}

void snd_audiopci_cleanup(void)
{
	int dev;

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

