/*
 *  PCI support functions
 *  Copyright (c) by 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.
 *
 */

#include "driver.h"
#include "sndpci.h"

#ifdef CONFIG_PCI

#ifndef LINUX_2_1

int snd_pci_find_device(unsigned int vendor, unsigned int device,
                        unsigned int index, struct snd_pci_dev *dev)
{
	unsigned char bus, devfn;
	unsigned char cmd, tmp;
	unsigned int l;
	int idx;

	if (!dev || index < 0)
		return -EINVAL;
	if (pcibios_find_device(vendor, device, index, &bus, &devfn) == 0) {
		memset(dev, 0, sizeof(struct snd_pci_dev));
		dev->bus = bus;
		dev->devfn = devfn;
		dev->vendor = vendor;
		dev->device = device;
		/* non-destructively determine if device can be a master: */
		snd_pci_read_config_byte(dev, PCI_COMMAND, &cmd);
		cmd |= PCI_COMMAND_MASTER;
		snd_pci_write_config_byte(dev, PCI_COMMAND, cmd);
		snd_pci_read_config_byte(dev, PCI_COMMAND, &tmp);
		dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
		snd_pci_write_config_byte(dev, PCI_COMMAND, cmd);
		/* check to see if this device is a PCI-PCI bridge: */
		snd_pci_read_config_dword(dev, PCI_CLASS_REVISION, &l);
		l = l >> 8;	/* upper 3 bytes */
		dev->class = l;
		/* determine interrupt line */
		snd_pci_read_config_byte(dev, PCI_INTERRUPT_LINE,
						((unsigned char *) &dev->irq));
		/* determine base addresses */
		for (idx = 0; idx < 6; idx++)
			snd_pci_read_config_dword(dev,
				  PCI_BASE_ADDRESS_0 + (idx << 2),
				  ((unsigned int *) &dev->base_address[idx]));
		return 0;
	}
	return -ENODEV;
}

#else

int snd_pci_find_device(unsigned int vendor, unsigned int device,
			unsigned int index, struct snd_pci_dev *dev)
{
	struct pci_dev *pci_dev;
	int idx;

	if (!dev || index < 0)
		return -EINVAL;
	pci_dev = NULL;
	do {
		pci_dev = pci_find_device(vendor, device, pci_dev);
		if (pci_dev) {
			if (!index) {
				memset(dev, 0, sizeof(struct snd_pci_dev));
				dev->pci_dev = pci_dev;
				dev->bus = pci_dev->bus->number;
				dev->devfn = pci_dev->devfn;
				dev->vendor = vendor;
				dev->device = device;
				dev->class = pci_dev->class;
				dev->master = pci_dev->master;
				dev->irq = pci_dev->irq;
				for (idx = 0; idx < 6; idx++)
					dev->base_address[idx] = pci_dev->base_address[idx];
				return 0;
			} else {
				index--;
			}
		}
	} while (pci_dev);
	return -ENODEV;
}

#endif

#endif				/* CONFIG_PCI */
