/*
 *   ALSA driver for RME Digi9652 audio interfaces 
 *
 *	Copyright (c) 1999 IEM - Winfried Ritsch
 *      Copyright (c) 1999-2000  Paul Barton-Davis
 *
 *   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/rme9652.h"
#include "../include/info.h"
#include "../include/initval.h"

static rme9652_t *snd_rme9652_cards[SND_CARDS] = SND_DEFAULT_PTR;

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_enable[SND_CARDS] = {1, [1 ... (SND_CARDS - 1)] = 0};

MODULE_PARM(snd_index, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_index, "Index value for RME Digi9652 (Hammerfall) soundcard.");
MODULE_PARM(snd_id, "1-" __MODULE_STRING(SND_CARDS) "s");
MODULE_PARM_DESC(snd_id, "ID string for RME Digi9652 (Hammerfall) soundcard.");
MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SND_CARDS) "i");
MODULE_PARM_DESC(snd_enable, 
		 "Enable/disable specific RME96{52,36} soundcards.\n"
		 "\tUse a list like \"0,1\" to specify on (1) or off (0) for "
		 "up to " __MODULE_STRING(SND_CARDS) " cards");
MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>, Winfried Ritsch");
MODULE_DESCRIPTION("\
Driver: RME Digi9652/Digi9636 (Hammerfall + Hammerfall-Light)\n\
PCI: 0x10ee=0x3fc4\n\
");

static void snd_rme9652_use_inc (snd_card_t *);
static void snd_rme9652_use_dec (snd_card_t *);
static int  snd_rme9652_detect (snd_card_t *, rme9652_t *s, 
				unsigned int, 
				struct pci_dev **, 
				struct pci_dev *);
static int  snd_rme9652_probe (int dev, rme9652_t *, 
			       struct pci_dev **, 
			       struct pci_dev *);

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

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

static int snd_rme9652_detect(snd_card_t * card, 
			      rme9652_t *rme9652,
			      unsigned int device_id,
			      struct pci_dev **pci_info,
			      struct pci_dev *prev_dev)
{
	unsigned short rev;

	if ((*pci_info = pci_find_device (PCI_VENDOR_ID_XILINX,
					  device_id,
					  prev_dev)) == NULL) {
		return -ENODEV;
	}

	if ((rme9652->port = pci_resource_start(*pci_info, 0)) == 0) {
		return -ENODEV;
	}

	if ((*pci_info)->irq == 0) {
		return -ENODEV;
	}

	/* Differentiate between the standard Hammerfall, and the
	   "Light", which does not have the expansion board. This
	   method comes from information received from Mathhias
	   Clausen at RME.
	*/

	pci_read_config_word (*pci_info, PCI_CLASS_REVISION, &rev);

	switch (rev & 0xff) {
	case 8:
		card->type = SND_CARD_TYPE_HAMMERFALL_LIGHT;
		rme9652->card_name = "RME Digi9636";
		rme9652->nchannels = RME9652_HAMMERFALL_LIGHT_NCHANNELS;
		break;
	case 3:
	default:
		card->type = SND_CARD_TYPE_HAMMERFALL;
		rme9652->card_name = "RME Digi9652";
		rme9652->nchannels = RME9652_HAMMERFALL_NCHANNELS;
	}

	rme9652->pci = *pci_info;
	return 0;
}

static int snd_rme9652_probe(int dev, rme9652_t *rme9652, 
			     struct pci_dev **pci_info,
			     struct pci_dev *prev_dev)
{
	snd_card_t *card;
	int err;

	card = snd_card_new (snd_index[dev], snd_id[dev],
			     snd_rme9652_use_inc, 
			     snd_rme9652_use_dec);

	if (!card) {
		return -ENOMEM;
	}

	rme9652->pci = NULL;
	
	if (snd_rme9652_detect (card, rme9652, 
				PCI_DEVICE_ID_XILINX_HAMMERFALL, 
				pci_info,
				prev_dev) < 0) {
		goto __nodev;
	}

	if (!snd_enable[dev]) {
		snd_printk ("%s: card #%d not configured "
			    "by request\n", 
			    rme9652->card_name,
			    dev+1);
		snd_card_free (card);

		/* tell caller that there was no error,
		   but that this card was not configured.
		   the error code is just intended to be
		   a particular unlikely one.
		*/

		return -EMFILE; 
	}

	if (pci_enable_device(*pci_info))
		goto __nodev;

	if ((err = snd_register_interrupt (card,
					   rme9652->card_name,
					   rme9652->pci->irq,
					   SND_IRQ_TYPE_PCI, 
					   snd_rme9652_interrupt,
					   rme9652, 
					   NULL,
					   &rme9652->irqptr)) < 0) {
		goto __nodev;
	}

	if (snd_rme9652_create (card, dev, rme9652) < 0) {
		goto __nodev;
	}

	strcpy(card->abbreviation, "Hammerfall");
	strcpy(card->shortname, rme9652->card_name);

	sprintf(card->longname, "%s at 0x%lx, irq %li",
		card->shortname,
		rme9652->port,
		rme9652->irqptr->irq);
	
	if (!snd_card_register(card)) {
		return 0;
	}

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

static int __init alsa_card_hammerfall_init(void)
{
	int dev, cards;
	rme9652_t *rme9652;
	struct pci_dev *pci_info;
	struct pci_dev *prev_dev = 0;
	int err;

	for (dev = cards = 0; dev < SND_CARDS; dev++) {
		rme9652 = (rme9652_t *)
			snd_kcalloc(sizeof(rme9652_t), GFP_KERNEL);

		rme9652->last_spdif_sample_rate = -1;
		rme9652->last_adat_sample_rate = -1;

		if (!rme9652) {
			return -ENOMEM;
		}

		if ((err = snd_rme9652_probe (dev, rme9652, &pci_info, prev_dev)) < 0) {
			if (err != -EMFILE) { 
				break;
			} else {
				prev_dev = pci_info;
				continue;
			}
		}

		snd_rme9652_cards[dev] = rme9652;
		prev_dev = pci_info;
		cards++;
	}

	if (!cards) {
		snd_printk ("RME Digi9652/Digi9636: no cards found\n");
		return -ENODEV;
	}

	return 0;
}

static void __exit alsa_card_hammerfall_exit(void)
{
	int dev;
	rme9652_t *rme9652;

	for (dev = 0; dev < SND_CARDS; dev++) {
		rme9652 = snd_rme9652_cards[dev];
		snd_rme9652_cards[dev] = NULL;

		if (rme9652) {
			snd_rme9652_free (rme9652);
		}
	}

	return;
}

module_init(alsa_card_hammerfall_init)
module_exit(alsa_card_hammerfall_exit)
