18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  The driver for the Yamaha's DS1/DS1E cards
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/init.h>
88c2ecf20Sopenharmony_ci#include <linux/pci.h>
98c2ecf20Sopenharmony_ci#include <linux/time.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <sound/core.h>
128c2ecf20Sopenharmony_ci#include "ymfpci.h"
138c2ecf20Sopenharmony_ci#include <sound/mpu401.h>
148c2ecf20Sopenharmony_ci#include <sound/opl3.h>
158c2ecf20Sopenharmony_ci#include <sound/initval.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Yamaha DS-1 PCI");
198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
208c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Yamaha,YMF724},"
218c2ecf20Sopenharmony_ci		"{Yamaha,YMF724F},"
228c2ecf20Sopenharmony_ci		"{Yamaha,YMF740},"
238c2ecf20Sopenharmony_ci		"{Yamaha,YMF740C},"
248c2ecf20Sopenharmony_ci		"{Yamaha,YMF744},"
258c2ecf20Sopenharmony_ci		"{Yamaha,YMF754}}");
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
288c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
298c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
308c2ecf20Sopenharmony_cistatic long fm_port[SNDRV_CARDS];
318c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS];
328c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
338c2ecf20Sopenharmony_cistatic long joystick_port[SNDRV_CARDS];
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_cistatic bool rear_switch[SNDRV_CARDS];
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the Yamaha DS-1 PCI soundcard.");
398c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the Yamaha DS-1 PCI soundcard.");
418c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Yamaha DS-1 soundcard.");
438c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444);
448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 Port.");
458c2ecf20Sopenharmony_cimodule_param_hw_array(fm_port, long, ioport, NULL, 0444);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM OPL-3 Port.");
478c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
488c2ecf20Sopenharmony_cimodule_param_hw_array(joystick_port, long, ioport, NULL, 0444);
498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick_port, "Joystick port address");
508c2ecf20Sopenharmony_ci#endif
518c2ecf20Sopenharmony_cimodule_param_array(rear_switch, bool, NULL, 0444);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_ymfpci_ids[] = {
558c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(YAMAHA, 0x0004), 0, },   /* YMF724 */
568c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(YAMAHA, 0x000d), 0, },   /* YMF724F */
578c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(YAMAHA, 0x000a), 0, },   /* YMF740 */
588c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(YAMAHA, 0x000c), 0, },   /* YMF740C */
598c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(YAMAHA, 0x0010), 0, },   /* YMF744 */
608c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(YAMAHA, 0x0012), 0, },   /* YMF754 */
618c2ecf20Sopenharmony_ci	{ 0, }
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_ymfpci_ids);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
678c2ecf20Sopenharmony_cistatic int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev,
688c2ecf20Sopenharmony_ci				      int legacy_ctrl, int legacy_ctrl2)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct gameport *gp;
718c2ecf20Sopenharmony_ci	struct resource *r = NULL;
728c2ecf20Sopenharmony_ci	int io_port = joystick_port[dev];
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!io_port)
758c2ecf20Sopenharmony_ci		return -ENODEV;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (chip->pci->device >= 0x0010) { /* YMF 744/754 */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (io_port == 1) {
808c2ecf20Sopenharmony_ci			/* auto-detect */
818c2ecf20Sopenharmony_ci			io_port = pci_resource_start(chip->pci, 2);
828c2ecf20Sopenharmony_ci			if (!io_port)
838c2ecf20Sopenharmony_ci				return -ENODEV;
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci	} else {
868c2ecf20Sopenharmony_ci		if (io_port == 1) {
878c2ecf20Sopenharmony_ci			/* auto-detect */
888c2ecf20Sopenharmony_ci			for (io_port = 0x201; io_port <= 0x205; io_port++) {
898c2ecf20Sopenharmony_ci				if (io_port == 0x203)
908c2ecf20Sopenharmony_ci					continue;
918c2ecf20Sopenharmony_ci				r = request_region(io_port, 1, "YMFPCI gameport");
928c2ecf20Sopenharmony_ci				if (r)
938c2ecf20Sopenharmony_ci					break;
948c2ecf20Sopenharmony_ci			}
958c2ecf20Sopenharmony_ci			if (!r) {
968c2ecf20Sopenharmony_ci				dev_err(chip->card->dev,
978c2ecf20Sopenharmony_ci					"no gameport ports available\n");
988c2ecf20Sopenharmony_ci				return -EBUSY;
998c2ecf20Sopenharmony_ci			}
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci		switch (io_port) {
1028c2ecf20Sopenharmony_ci		case 0x201: legacy_ctrl2 |= 0 << 6; break;
1038c2ecf20Sopenharmony_ci		case 0x202: legacy_ctrl2 |= 1 << 6; break;
1048c2ecf20Sopenharmony_ci		case 0x204: legacy_ctrl2 |= 2 << 6; break;
1058c2ecf20Sopenharmony_ci		case 0x205: legacy_ctrl2 |= 3 << 6; break;
1068c2ecf20Sopenharmony_ci		default:
1078c2ecf20Sopenharmony_ci			dev_err(chip->card->dev,
1088c2ecf20Sopenharmony_ci				"invalid joystick port %#x", io_port);
1098c2ecf20Sopenharmony_ci			return -EINVAL;
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (!r) {
1148c2ecf20Sopenharmony_ci		r = request_region(io_port, 1, "YMFPCI gameport");
1158c2ecf20Sopenharmony_ci		if (!r) {
1168c2ecf20Sopenharmony_ci			dev_err(chip->card->dev,
1178c2ecf20Sopenharmony_ci				"joystick port %#x is in use.\n", io_port);
1188c2ecf20Sopenharmony_ci			return -EBUSY;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	chip->gameport = gp = gameport_allocate_port();
1238c2ecf20Sopenharmony_ci	if (!gp) {
1248c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
1258c2ecf20Sopenharmony_ci			"cannot allocate memory for gameport\n");
1268c2ecf20Sopenharmony_ci		release_and_free_resource(r);
1278c2ecf20Sopenharmony_ci		return -ENOMEM;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	gameport_set_name(gp, "Yamaha YMF Gameport");
1328c2ecf20Sopenharmony_ci	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1338c2ecf20Sopenharmony_ci	gameport_set_dev_parent(gp, &chip->pci->dev);
1348c2ecf20Sopenharmony_ci	gp->io = io_port;
1358c2ecf20Sopenharmony_ci	gameport_set_port_data(gp, r);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (chip->pci->device >= 0x0010) /* YMF 744/754 */
1388c2ecf20Sopenharmony_ci		pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, io_port);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, legacy_ctrl | YMFPCI_LEGACY_JPEN);
1418c2ecf20Sopenharmony_ci	pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	gameport_register_port(chip->gameport);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_civoid snd_ymfpci_free_gameport(struct snd_ymfpci *chip)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	if (chip->gameport) {
1518c2ecf20Sopenharmony_ci		struct resource *r = gameport_get_port_data(chip->gameport);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		gameport_unregister_port(chip->gameport);
1548c2ecf20Sopenharmony_ci		chip->gameport = NULL;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		release_and_free_resource(r);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci#else
1608c2ecf20Sopenharmony_cistatic inline int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev, int l, int l2) { return -ENOSYS; }
1618c2ecf20Sopenharmony_civoid snd_ymfpci_free_gameport(struct snd_ymfpci *chip) { }
1628c2ecf20Sopenharmony_ci#endif /* SUPPORT_JOYSTICK */
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int snd_card_ymfpci_probe(struct pci_dev *pci,
1658c2ecf20Sopenharmony_ci				 const struct pci_device_id *pci_id)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	static int dev;
1688c2ecf20Sopenharmony_ci	struct snd_card *card;
1698c2ecf20Sopenharmony_ci	struct resource *fm_res = NULL;
1708c2ecf20Sopenharmony_ci	struct resource *mpu_res = NULL;
1718c2ecf20Sopenharmony_ci	struct snd_ymfpci *chip;
1728c2ecf20Sopenharmony_ci	struct snd_opl3 *opl3;
1738c2ecf20Sopenharmony_ci	const char *str, *model;
1748c2ecf20Sopenharmony_ci	int err;
1758c2ecf20Sopenharmony_ci	u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
1788c2ecf20Sopenharmony_ci		return -ENODEV;
1798c2ecf20Sopenharmony_ci	if (!enable[dev]) {
1808c2ecf20Sopenharmony_ci		dev++;
1818c2ecf20Sopenharmony_ci		return -ENOENT;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
1858c2ecf20Sopenharmony_ci			   0, &card);
1868c2ecf20Sopenharmony_ci	if (err < 0)
1878c2ecf20Sopenharmony_ci		return err;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	switch (pci_id->device) {
1908c2ecf20Sopenharmony_ci	case 0x0004: str = "YMF724";  model = "DS-1"; break;
1918c2ecf20Sopenharmony_ci	case 0x000d: str = "YMF724F"; model = "DS-1"; break;
1928c2ecf20Sopenharmony_ci	case 0x000a: str = "YMF740";  model = "DS-1L"; break;
1938c2ecf20Sopenharmony_ci	case 0x000c: str = "YMF740C"; model = "DS-1L"; break;
1948c2ecf20Sopenharmony_ci	case 0x0010: str = "YMF744";  model = "DS-1S"; break;
1958c2ecf20Sopenharmony_ci	case 0x0012: str = "YMF754";  model = "DS-1E"; break;
1968c2ecf20Sopenharmony_ci	default: model = str = "???"; break;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	legacy_ctrl = 0;
2008c2ecf20Sopenharmony_ci	legacy_ctrl2 = 0x0800;	/* SBEN = 0, SMOD = 01, LAD = 0 */
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (pci_id->device >= 0x0010) { /* YMF 744/754 */
2038c2ecf20Sopenharmony_ci		if (fm_port[dev] == 1) {
2048c2ecf20Sopenharmony_ci			/* auto-detect */
2058c2ecf20Sopenharmony_ci			fm_port[dev] = pci_resource_start(pci, 1);
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci		if (fm_port[dev] > 0)
2088c2ecf20Sopenharmony_ci			fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3");
2098c2ecf20Sopenharmony_ci		if (fm_res) {
2108c2ecf20Sopenharmony_ci			legacy_ctrl |= YMFPCI_LEGACY_FMEN;
2118c2ecf20Sopenharmony_ci			pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]);
2128c2ecf20Sopenharmony_ci		}
2138c2ecf20Sopenharmony_ci		if (mpu_port[dev] == 1) {
2148c2ecf20Sopenharmony_ci			/* auto-detect */
2158c2ecf20Sopenharmony_ci			mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci		if (mpu_port[dev] > 0)
2188c2ecf20Sopenharmony_ci			mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401");
2198c2ecf20Sopenharmony_ci		if (mpu_res) {
2208c2ecf20Sopenharmony_ci			legacy_ctrl |= YMFPCI_LEGACY_MEN;
2218c2ecf20Sopenharmony_ci			pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]);
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci	} else {
2248c2ecf20Sopenharmony_ci		switch (fm_port[dev]) {
2258c2ecf20Sopenharmony_ci		case 0x388: legacy_ctrl2 |= 0; break;
2268c2ecf20Sopenharmony_ci		case 0x398: legacy_ctrl2 |= 1; break;
2278c2ecf20Sopenharmony_ci		case 0x3a0: legacy_ctrl2 |= 2; break;
2288c2ecf20Sopenharmony_ci		case 0x3a8: legacy_ctrl2 |= 3; break;
2298c2ecf20Sopenharmony_ci		default: fm_port[dev] = 0; break;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci		if (fm_port[dev] > 0)
2328c2ecf20Sopenharmony_ci			fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3");
2338c2ecf20Sopenharmony_ci		if (fm_res) {
2348c2ecf20Sopenharmony_ci			legacy_ctrl |= YMFPCI_LEGACY_FMEN;
2358c2ecf20Sopenharmony_ci		} else {
2368c2ecf20Sopenharmony_ci			legacy_ctrl2 &= ~YMFPCI_LEGACY2_FMIO;
2378c2ecf20Sopenharmony_ci			fm_port[dev] = 0;
2388c2ecf20Sopenharmony_ci		}
2398c2ecf20Sopenharmony_ci		switch (mpu_port[dev]) {
2408c2ecf20Sopenharmony_ci		case 0x330: legacy_ctrl2 |= 0 << 4; break;
2418c2ecf20Sopenharmony_ci		case 0x300: legacy_ctrl2 |= 1 << 4; break;
2428c2ecf20Sopenharmony_ci		case 0x332: legacy_ctrl2 |= 2 << 4; break;
2438c2ecf20Sopenharmony_ci		case 0x334: legacy_ctrl2 |= 3 << 4; break;
2448c2ecf20Sopenharmony_ci		default: mpu_port[dev] = 0; break;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci		if (mpu_port[dev] > 0)
2478c2ecf20Sopenharmony_ci			mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401");
2488c2ecf20Sopenharmony_ci		if (mpu_res) {
2498c2ecf20Sopenharmony_ci			legacy_ctrl |= YMFPCI_LEGACY_MEN;
2508c2ecf20Sopenharmony_ci		} else {
2518c2ecf20Sopenharmony_ci			legacy_ctrl2 &= ~YMFPCI_LEGACY2_MPUIO;
2528c2ecf20Sopenharmony_ci			mpu_port[dev] = 0;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	if (mpu_res) {
2568c2ecf20Sopenharmony_ci		legacy_ctrl |= YMFPCI_LEGACY_MIEN;
2578c2ecf20Sopenharmony_ci		legacy_ctrl2 |= YMFPCI_LEGACY2_IMOD;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci	pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl);
2608c2ecf20Sopenharmony_ci	pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
2618c2ecf20Sopenharmony_ci	pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
2628c2ecf20Sopenharmony_ci	err = snd_ymfpci_create(card, pci, old_legacy_ctrl, &chip);
2638c2ecf20Sopenharmony_ci	if (err  < 0) {
2648c2ecf20Sopenharmony_ci		release_and_free_resource(mpu_res);
2658c2ecf20Sopenharmony_ci		release_and_free_resource(fm_res);
2668c2ecf20Sopenharmony_ci		goto free_card;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	chip->fm_res = fm_res;
2698c2ecf20Sopenharmony_ci	chip->mpu_res = mpu_res;
2708c2ecf20Sopenharmony_ci	card->private_data = chip;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	strcpy(card->driver, str);
2738c2ecf20Sopenharmony_ci	sprintf(card->shortname, "Yamaha %s (%s)", model, str);
2748c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, irq %i",
2758c2ecf20Sopenharmony_ci		card->shortname,
2768c2ecf20Sopenharmony_ci		chip->reg_area_phys,
2778c2ecf20Sopenharmony_ci		chip->irq);
2788c2ecf20Sopenharmony_ci	err = snd_ymfpci_pcm(chip, 0);
2798c2ecf20Sopenharmony_ci	if (err < 0)
2808c2ecf20Sopenharmony_ci		goto free_card;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	err = snd_ymfpci_pcm_spdif(chip, 1);
2838c2ecf20Sopenharmony_ci	if (err < 0)
2848c2ecf20Sopenharmony_ci		goto free_card;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	err = snd_ymfpci_mixer(chip, rear_switch[dev]);
2878c2ecf20Sopenharmony_ci	if (err < 0)
2888c2ecf20Sopenharmony_ci		goto free_card;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (chip->ac97->ext_id & AC97_EI_SDAC) {
2918c2ecf20Sopenharmony_ci		err = snd_ymfpci_pcm_4ch(chip, 2);
2928c2ecf20Sopenharmony_ci		if (err < 0)
2938c2ecf20Sopenharmony_ci			goto free_card;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		err = snd_ymfpci_pcm2(chip, 3);
2968c2ecf20Sopenharmony_ci		if (err < 0)
2978c2ecf20Sopenharmony_ci			goto free_card;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	err = snd_ymfpci_timer(chip, 0);
3008c2ecf20Sopenharmony_ci	if (err < 0)
3018c2ecf20Sopenharmony_ci		goto free_card;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (chip->mpu_res) {
3048c2ecf20Sopenharmony_ci		err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
3058c2ecf20Sopenharmony_ci					  mpu_port[dev],
3068c2ecf20Sopenharmony_ci					  MPU401_INFO_INTEGRATED |
3078c2ecf20Sopenharmony_ci					  MPU401_INFO_IRQ_HOOK,
3088c2ecf20Sopenharmony_ci					  -1, &chip->rawmidi);
3098c2ecf20Sopenharmony_ci		if (err < 0) {
3108c2ecf20Sopenharmony_ci			dev_warn(card->dev,
3118c2ecf20Sopenharmony_ci				 "cannot initialize MPU401 at 0x%lx, skipping...\n",
3128c2ecf20Sopenharmony_ci				 mpu_port[dev]);
3138c2ecf20Sopenharmony_ci			legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
3148c2ecf20Sopenharmony_ci			pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	if (chip->fm_res) {
3188c2ecf20Sopenharmony_ci		err = snd_opl3_create(card,
3198c2ecf20Sopenharmony_ci				      fm_port[dev],
3208c2ecf20Sopenharmony_ci				      fm_port[dev] + 2,
3218c2ecf20Sopenharmony_ci				      OPL3_HW_OPL3, 1, &opl3);
3228c2ecf20Sopenharmony_ci		if (err < 0) {
3238c2ecf20Sopenharmony_ci			dev_warn(card->dev,
3248c2ecf20Sopenharmony_ci				 "cannot initialize FM OPL3 at 0x%lx, skipping...\n",
3258c2ecf20Sopenharmony_ci				 fm_port[dev]);
3268c2ecf20Sopenharmony_ci			legacy_ctrl &= ~YMFPCI_LEGACY_FMEN;
3278c2ecf20Sopenharmony_ci			pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
3288c2ecf20Sopenharmony_ci		} else {
3298c2ecf20Sopenharmony_ci			err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
3308c2ecf20Sopenharmony_ci			if (err < 0) {
3318c2ecf20Sopenharmony_ci				dev_err(card->dev, "cannot create opl3 hwdep\n");
3328c2ecf20Sopenharmony_ci				goto free_card;
3338c2ecf20Sopenharmony_ci			}
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	snd_ymfpci_create_gameport(chip, dev, legacy_ctrl, legacy_ctrl2);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	err = snd_card_register(card);
3408c2ecf20Sopenharmony_ci	if (err < 0)
3418c2ecf20Sopenharmony_ci		goto free_card;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, card);
3448c2ecf20Sopenharmony_ci	dev++;
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cifree_card:
3488c2ecf20Sopenharmony_ci	snd_card_free(card);
3498c2ecf20Sopenharmony_ci	return err;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void snd_card_ymfpci_remove(struct pci_dev *pci)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	snd_card_free(pci_get_drvdata(pci));
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic struct pci_driver ymfpci_driver = {
3588c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
3598c2ecf20Sopenharmony_ci	.id_table = snd_ymfpci_ids,
3608c2ecf20Sopenharmony_ci	.probe = snd_card_ymfpci_probe,
3618c2ecf20Sopenharmony_ci	.remove = snd_card_ymfpci_remove,
3628c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3638c2ecf20Sopenharmony_ci	.driver = {
3648c2ecf20Sopenharmony_ci		.pm = &snd_ymfpci_pm,
3658c2ecf20Sopenharmony_ci	},
3668c2ecf20Sopenharmony_ci#endif
3678c2ecf20Sopenharmony_ci};
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cimodule_pci_driver(ymfpci_driver);
370