/*
 *   ALSA driver for ICEnsemble ICE1712 (Envy24)
 *
 *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.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.
 *
 */      

#define SND_MAIN_OBJECT_FILE

#include "../include/driver.h"
#include "../include/pcm.h"
#include "../include/hwdep.h"
#include "../include/mpu401.h"
#include "../include/ice1712.h"
#include "../include/info.h"
#include "../include/initval.h"

MODULE_DESCRIPTION("\
Driver: ICEnsemble ICE1712 (Envy24)\n\
Card: MidiMan M Audio Delta 1010\n\
Card: MidiMan M Audio Delta DiO 2496\n\
Card: MidiMan M Audio Delta 66\n\
Card: MidiMan M Audio Delta 44\n\
PCI: 0x1412=0x1712\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_conp_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_conc_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 128};
int snd_prop_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 256};
int snd_proc_frame_size[SND_CARDS] = {[0 ... (SND_CARDS - 1)] = 256};
MODULE_PARM(snd_index, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_index, "Index value for ICE1712 soundcard.");
MODULE_PARM(snd_id, "1-" __MODULE_STRING(SND_CARDS) "s");
MODULE_PARM_DESC(snd_id, "ID string for ICE1712 soundcard.");
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_PARM(snd_conp_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_conp_frame_size, "Consumer playback frame size in kB.");
MODULE_PARM(snd_conc_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_conc_frame_size, "Consumer capture frame size in kB.");
MODULE_PARM(snd_prop_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_prop_frame_size, "Professional playback frame size in kB.");
MODULE_PARM(snd_proc_frame_size, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_proc_frame_size, "Professional capture frame size in kB.");

typedef struct snd_ice1712_card {
	struct pci_dev *pci;
	snd_irq_t *irqptr;
	snd_dma_t *dma_conp;	/* consumer playback */
	snd_dma_t *dma_conc;	/* consumer capture */
	snd_dma_t *dma_prop;	/* professional playback */
	snd_dma_t *dma_proc;	/* professional capture */
	snd_card_t *card;
	ice1712_t *ice1712;
	snd_pcm_t *pcm;
	snd_pcm_t *pcm_pro;
	snd_kmixer_t *mixer;
	snd_rawmidi_t *midi_uart;
	snd_rawmidi_t *midi_uart2;
} ice1712_card_t;

static ice1712_card_t *snd_ice1712_cards[SND_CARDS] = SND_DEFAULT_PTR;

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

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

static int __init snd_ice1712_detect(snd_card_t * card, ice1712_card_t *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), 32, "ICE1712 - Controller", NULL) < 0)
		goto __nodev;
	if (snd_register_ioport(card, pci_resource_start(scard->pci, 1), 16, "ICE1712 - DDMA", NULL) < 0)
		goto __nodev;
	if (snd_register_ioport(card, pci_resource_start(scard->pci, 2), 16, "ICE1712 - DMA path", NULL) < 0)
		goto __nodev;
	if (snd_register_ioport(card, pci_resource_start(scard->pci, 3), 64, "ICE1712 - Professional", NULL) < 0)
		goto __nodev;
	return 0;
      __nodev:
	snd_unregister_ioports(card);
	return -ENODEV;
}

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

	if (scard == NULL || scard->ice1712 == NULL)
		return;
	snd_ice1712_interrupt(scard->ice1712);
}

static int __init snd_ice1712_resources(snd_card_t * card,
					ice1712_card_t *scard,
					int dev)
{
	int err;

	if ((err = snd_register_interrupt(card,
			"ICE1712", scard->pci->irq,
			SND_IRQ_TYPE_PCI, snd_ice1712_card_interrupt,
			scard, NULL, &scard->irqptr)) < 0)
		return err;
	if ((err = snd_register_dma_channel(card,
			"ICE1712 - consumer playback", 0,
			SND_DMA_TYPE_PCI, snd_conp_frame_size[dev],
			NULL, &scard->dma_conp)) < 0)
		return err;
	scard->dma_conp->multi = 1;
	scard->dma_conp->addressbits = 28;	/* 256MB */
	if ((err = snd_register_dma_channel(card,
			"ICE1712 - consumer capture", 1,
			SND_DMA_TYPE_PCI, snd_conc_frame_size[dev],
			NULL, &scard->dma_conc)) < 0)
		return err;
	scard->dma_conc->addressbits = 28;	/* 256MB */
	if ((err = snd_register_dma_channel(card,
			"ICE1712 - professional playback", 2,
			SND_DMA_TYPE_PCI, snd_prop_frame_size[dev],
			NULL, &scard->dma_prop)) < 0)
		return err;
	scard->dma_prop->addressbits = 28;	/* 256MB */
	if ((err = snd_register_dma_channel(card,
			"ICE1712 - professional capture", 3,
			SND_DMA_TYPE_PCI, snd_proc_frame_size[dev],
			NULL, &scard->dma_proc)) < 0)
		return err;
	scard->dma_proc->addressbits = 28;	/* 256MB */
	return 0;
}

static int __init snd_ice1712_probe(int dev, ice1712_card_t *scard)
{
	snd_card_t *card;
	snd_pcm_t *pcm = NULL, *pcm_pro = NULL;
	snd_kmixer_t *mixer = NULL;
	snd_rawmidi_t *midi_uart = NULL, *midi_uart2 = NULL;
	ice1712_t *ice;
	int pcm_dev = 0, mixer_dev = 0;

	card = snd_card_new(snd_index[dev], snd_id[dev],
			    snd_ice1712_use_inc, snd_ice1712_use_dec);
	if (card == NULL)
		return -ENOMEM;
	card->type = SND_CARD_TYPE_ICE1712;
	do {
		if (!snd_ice1712_detect(card, scard, PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712))
			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_ice1712_resources(card, scard, dev) < 0) {
		snd_card_free(card);
		return -ENODEV;
	}

	if (snd_ice1712_create(card, scard->pci,
			       scard->dma_conp,
			       scard->dma_conc,
			       scard->dma_prop,
			       scard->dma_proc,
			       scard->irqptr,
			       &scard->ice1712) < 0)
		goto __nodev;
	ice = scard->ice1712;

	if (snd_ice1712_mixer(ice, mixer_dev++, 1, &pcm_dev, &mixer) < 0)
		goto __nodev;
	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
		if (snd_ice1712_pcm(ice, pcm_dev++, &pcm) < 0)
			goto __nodev;

	if (snd_ice1712_pcm_profi(ice, pcm_dev++, &pcm_pro) < 0)
		goto __nodev;
	
	strcpy(card->abbreviation, "ICE1712");
	strcpy(card->shortname, "ICEnsemble ICE1712");
	
	switch (ice->eeprom.subvendor) {
	case ICE1712_SUBDEVICE_DELTA1010:
		strcpy(card->shortname, "M Audio Delta 1010");
		break;
	case ICE1712_SUBDEVICE_DELTADIO2496:
		strcpy(card->shortname, "M Audio Delta DiO 2496");
		goto __no_mpu401;
	case ICE1712_SUBDEVICE_DELTA66:
		strcpy(card->shortname, "M Audio Delta 66");
		goto __no_mpu401;
	case ICE1712_SUBDEVICE_DELTA44:
		strcpy(card->shortname, "M Audio Delta 44");
		goto __no_mpu401;
	}

	if (snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
				ICEREG(ice, MPU1_CTRL),
				scard->irqptr->irq,
				&midi_uart) < 0)
		goto __nodev;

	if (ice->eeprom.codec & ICE1712_CFG_2xMPU401)
		if (snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
					ICEREG(ice, MPU2_CTRL),
					scard->irqptr->irq,
					&midi_uart2) < 0)
			goto __nodev;

      __no_mpu401:
	sprintf(card->longname, "%s at 0x%lx, irq %li",
		card->shortname, (long unsigned int)ice->port, scard->irqptr->irq);

	if (!snd_card_register(card)) {
		scard->card = card;
		scard->mixer = mixer;
		scard->pcm = pcm;
		scard->pcm_pro = pcm_pro;
		scard->midi_uart = ice->rmidi[0] = midi_uart;
		scard->midi_uart2 = ice->rmidi[1] = midi_uart2;
		return 0;
	}
	goto __nodev;

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

#ifdef MODULE

static int __exit snd_ice1712_card_free(int dev)
{
	ice1712_card_t *scard;

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

#endif

static int __init alsa_card_ice1712_init(void)
{
	int dev, cards;
	ice1712_card_t *scard;

	for (dev = cards = 0; dev < SND_CARDS; dev++) {
		scard = (ice1712_card_t *)
				snd_kcalloc(sizeof(ice1712_card_t), GFP_KERNEL);
		if (scard == NULL)
			continue;
		if (snd_ice1712_probe(dev, scard) < 0) {
			snd_kfree(scard);
			break;
		}
		snd_ice1712_cards[dev] = scard;
		cards++;
	}
	if (!cards) {
#ifdef MODULE
		snd_printk("ICE1712 soundcard #%i not found or device busy\n", dev + 1);
#endif
		return -ENODEV;
	}
	return 0;
}

static void __exit alsa_card_ice1712_exit(void)
{
	int dev;

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

module_init(alsa_card_ice1712_init)
module_exit(alsa_card_ice1712_exit)
