/*
 *  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 "../include/driver.h"
#include "../include/trident.h"
#include "../include/initval.h"

MODULE_DESCRIPTION("\
Driver: Trident 4D-WaveDX/NX\n\
Card: Generic Trident 4D-WaveNX\n\
Card: Generic Trident 4D-WaveDX\n\
Card: Generic SiS 7018 PCI Audio\n\
Card: Best Union - Miss Melody 4DWave PCI\n\
Card: HIS - 4DWave PCI\n\
Card: Warpspeed - ONSpeed 4DWave PCI\n\
Card: AzTech - PCI 64-Q3D\n\
Card: Addonics - SV 750\n\
Card: CHIC - True Sound 4Dwave\n\
Card: Shark - Predator4D-PCI\n\
Card: Jaton - SonicWave 4D\n\
Card: Hoontech SoundTrack Digital 4DWave NX\n\
PCI: 0x1023=0x2000\n\
PCI: 0x1023=0x2001\n\
PCI: 0x1039=0x7018\n\
");

int snd_index[SND_CARDS] = SND_DEFAULT_IDX;	/* Index 0-MAX */
char *snd_id[SND_CARDS] = SND_DEFAULT_STR;	/* ID for this card */
int snd_dac_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_adc_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 64};
int snd_foldback_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 64};
int snd_spdif_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_pcm_channels[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 32};
int snd_wavetable_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 2048};
MODULE_PARM(snd_index, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_index, "Index value for Trident 4DWave PCI soundcard.");
MODULE_PARM(snd_id, "1-" __MODULE_STRING(SND_CARDS) "s");
MODULE_PARM_DESC(snd_id, "ID string for Trident 4DWave PCI soundcard.");
MODULE_PARM(snd_dac_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_dac_frame_size, "DAC frame size in kB for Trident 4DWave DX/NX PCI soundcard.");
MODULE_PARM(snd_adc_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_adc_frame_size, "ADC frame size in kB for Trident 4DWave DX/NX PCI soundcard.");
MODULE_PARM(snd_foldback_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_foldback_frame_size, "Foldback frame size in kB for Trident 4DWave DX/NX PCI soundcard.");
MODULE_PARM(snd_spdif_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_spdif_frame_size, "S/PDIF frame size in kB for Trident 4DWave NX PCI soundcard.");
MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_pcm_channels, "Number of hardware channels assigned for PCM. [range=1-32]");
MODULE_PARM(snd_wavetable_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_wavetable_size, "Maximum memory size in kB for wavetable synth.");

struct snd_trident {
	struct pci_dev *pci;
	snd_irq_t *irqptr;
	snd_dma_t *dma1ptr;	/* DAC frame */
	snd_dma_t *dma2ptr;	/* ADC frame */
	snd_dma_t *dma3ptr;	/* Foldback frame */
	snd_dma_t *dma4ptr;	/* SPDIF frame */
	snd_card_t *card;
	trident_t *trident;
	snd_pcm_t *pcm;		/* ADC/DAC */
	snd_pcm_t *foldback;	/* Foldback */
	snd_pcm_t *spdif;	/* SPDIF */
	snd_kmixer_t *mixer;
	snd_rawmidi_t *rmidi;
};

static struct snd_trident *snd_trident_cards[SND_CARDS] = SND_DEFAULT_PTR;

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

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

static int __init snd_trident_detect(snd_card_t * card, struct snd_trident *scard,
				     unsigned short vendor, unsigned short device)
{
	if ((scard->pci = pci_find_device(vendor,
					  device,
					  scard->pci)) == NULL)
		return -ENODEV;
	if (snd_register_ioport(card, pci_resource_start(scard->pci, 0), 0x100, "Trident 4DWave PCI", NULL) < 0)
		goto __nodev;
	return 0;
      __nodev:
	snd_unregister_ioports(card);
	return -ENODEV;
}

static void snd_trid_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	struct snd_trident *scard = (struct snd_trident *) dev_id;

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

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

	dacSize = snd_dac_frame_size[dev];
	adcSize = snd_adc_frame_size[dev];
	foldbackSize = snd_foldback_frame_size[dev];
	spdifSize = snd_spdif_frame_size[dev];

	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,
					    TRIDENT_DMA_TYPE, dacSize,
					    NULL, &scard->dma1ptr)) < 0)
		return err;
	scard->dma1ptr->multi = 1;		/* enable multi-alloc */
	scard->dma1ptr->addressbits = 30;	/* 1GB */

	if ((err = snd_register_dma_channel(card,
				     "Trident 4DWave PCI - ADC frame", 1,
					    TRIDENT_DMA_TYPE, adcSize,
					    NULL, &scard->dma2ptr)) < 0)
		return err;
	scard->dma2ptr->addressbits = 30;	/* 1GB */

	if ((err = snd_register_dma_channel(card,
				     "Trident 4DWave PCI - Foldback frame", 1,
					    TRIDENT_DMA_TYPE, foldbackSize,
					    NULL, &scard->dma3ptr)) < 0)
		return err;
	scard->dma3ptr->multi = 1;		/* enable multi-alloc */
	scard->dma3ptr->addressbits = 30;	/* 1GB */

	if (card->type == SND_CARD_TYPE_TRID4DWAVENX) {
		scard->dma4ptr = NULL;
		if ((err = snd_register_dma_channel(card,
				     "Trident 4DWave PCI - S/PDIF frame", 0,
					    TRIDENT_DMA_TYPE, spdifSize,
					    NULL, &scard->dma4ptr)) < 0)
		return err;
	}

	return 0;
}

static int __init snd_trident_probe(int dev, struct snd_trident *scard)
{
	snd_card_t *card;
	snd_pcm_t *pcm = NULL;
	snd_pcm_t *foldback = NULL;
	snd_pcm_t *spdif = NULL;
	snd_kmixer_t *mixer = NULL;
	snd_rawmidi_t *rmidi = NULL;

	card = snd_card_new(snd_index[dev], snd_id[dev],
			    snd_trident_use_inc, snd_trident_use_dec);
	if (card == NULL)
		return -ENOMEM;
	scard->pci = NULL;
	do {
		if (!snd_trident_detect(card, scard, PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)) {
			card->type = SND_CARD_TYPE_TRID4DWAVEDX;
			break;
		}
	} while (scard->pci);
	if (scard->pci == NULL) {
		do {
			if (!snd_trident_detect(card, scard, PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)) {
				card->type = SND_CARD_TYPE_TRID4DWAVENX;
				break;
			}
		} while (scard->pci);
	}
	if (scard->pci == NULL) {
		do {
			if (!snd_trident_detect(card, scard, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018)) {
				card->type = SND_CARD_TYPE_SI_7018;
				break;
			}
		} while (scard->pci);
	}
	if (scard->pci == NULL) {
		snd_card_free(card);
		return -ENODEV;
	}
	if (pci_enable_device(scard->pci)) {
		snd_card_free(card);
		return -ENODEV;
	}
	if (snd_trident_resources(card, scard, dev) < 0) {
		snd_card_free(card);
		return -ENODEV;
	}
	if (snd_trident_create(card, scard->pci,
			       scard->dma1ptr, scard->dma2ptr, scard->dma3ptr, scard->dma4ptr,
			       scard->irqptr, snd_pcm_channels[dev], 
			       snd_wavetable_size[dev],
			       &scard->trident) < 0)
		goto __nodev;
	if (snd_trident_pcm(scard->trident, 0, &pcm) < 0)
		goto __nodev;
	if (snd_trident_mixer(scard->trident, 0, pcm, &mixer) < 0)
		goto __nodev;
	if (snd_trident_foldback_pcm(scard->trident, 1, &foldback) < 0)
		goto __nodev;
	if (card->type == SND_CARD_TYPE_TRID4DWAVENX) {
		if (snd_trident_spdif_pcm(scard->trident, 2, &spdif) < 0)
			goto __nodev;
	}
	if (snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
				scard->trident->midi_port,
				scard->irqptr->irq, &rmidi) < 0)
		goto __nodev;
	snd_trident_rawmidi(scard->trident, (mpu401_t *) rmidi->private_data);

#ifdef CONFIG_SND_SEQUENCER
	if (snd_trident_attach_synthesizer(scard->trident) < 0)
		goto __nodev;
#endif

	strcpy(card->abbreviation, "4DWave");
	if (card->type != SND_CARD_TYPE_SI_7018) {
		strcpy(card->shortname, "Trident 4DWave PCI");
		sprintf(card->longname, "Trident 4DWave%s at 0x%lx, irq %li",
			card->type == SND_CARD_TYPE_TRID4DWAVEDX ? "DX" : "NX",
			pci_resource_start(scard->pci, 0),
			scard->irqptr->irq);
	} else {
		strcpy(card->shortname, "SiS 7018");
		sprintf(card->longname, "SiS 7018 PCI Audio at 0x%lx, irq %li",
			pci_resource_start(scard->pci, 0),
			scard->irqptr->irq);
	}

	if (!snd_card_register(card)) {
		scard->card = card;
		scard->mixer = mixer;
		scard->pcm = pcm;
		scard->foldback = foldback;
		scard->spdif = spdif;
		scard->rmidi = scard->trident->rmidi = rmidi;
		return 0;
	}
	goto __nodev;

      __nodev:
	snd_card_free(card);
	return -ENXIO;
}

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

	scard = snd_trident_cards[dev];
	snd_trident_cards[dev] = NULL;
	if (scard) {
		snd_card_unregister(scard->card);
		snd_kfree(scard);
	}
	return 0;
}

static int __init alsa_card_trident_init(void)
{
	int dev, cards;
	struct snd_trident *scard;

	for (dev = cards = 0; dev < SND_CARDS; dev++) {
		scard = (struct snd_trident *)
		    snd_kcalloc(sizeof(struct snd_trident), GFP_KERNEL);
		if (scard == NULL)
			continue;

		if (snd_trident_probe(dev, scard) < 0) {
			snd_kfree(scard);
			break;
		}
		snd_trident_cards[dev] = scard;
		cards++;
	}
	if (!cards) {
#ifdef MODULE
		snd_printk("Trident 4DWave PCI soundcard #%i not found or device busy\n", dev + 1);
#endif
		return -ENODEV;
	}
	return 0;
}

static void __exit alsa_card_trident_exit(void)
{
	int dev;

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

module_init(alsa_card_trident_init)
module_exit(alsa_card_trident_exit)
