xref: /openbmc/linux/sound/isa/sb/sb8.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
4c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include <linux/init.h>
867be4458STakashi Iwai #include <linux/err.h>
95e24c1c1STakashi Iwai #include <linux/isa.h>
101da177e4SLinus Torvalds #include <linux/ioport.h>
1165a77217SPaul Gortmaker #include <linux/module.h>
121da177e4SLinus Torvalds #include <sound/core.h>
131da177e4SLinus Torvalds #include <sound/sb.h>
141da177e4SLinus Torvalds #include <sound/opl3.h>
151da177e4SLinus Torvalds #include <sound/initval.h>
161da177e4SLinus Torvalds 
17c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
181da177e4SLinus Torvalds MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
191da177e4SLinus Torvalds MODULE_LICENSE("GPL");
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
221da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
23a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
241da177e4SLinus Torvalds static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
251da177e4SLinus Torvalds static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
261da177e4SLinus Torvalds static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3 */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444);
291da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard.");
301da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444);
311da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
321da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444);
331da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
34e992ef57SDavid Howells module_param_hw_array(port, long, ioport, NULL, 0444);
351da177e4SLinus Torvalds MODULE_PARM_DESC(port, "Port # for SB8 driver.");
36e992ef57SDavid Howells module_param_hw_array(irq, int, irq, NULL, 0444);
371da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
38e992ef57SDavid Howells module_param_hw_array(dma8, int, dma, NULL, 0444);
391da177e4SLinus Torvalds MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds struct snd_sb8 {
421da177e4SLinus Torvalds 	struct resource *fm_res;	/* used to block FM i/o region for legacy cards */
4367be4458STakashi Iwai 	struct snd_sb *chip;
441da177e4SLinus Torvalds };
451da177e4SLinus Torvalds 
snd_sb8_interrupt(int irq,void * dev_id)467d12e780SDavid Howells static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
471da177e4SLinus Torvalds {
48029d64b0STakashi Iwai 	struct snd_sb *chip = dev_id;
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	if (chip->open & SB_OPEN_PCM) {
511da177e4SLinus Torvalds 		return snd_sb8dsp_interrupt(chip);
521da177e4SLinus Torvalds 	} else {
531da177e4SLinus Torvalds 		return snd_sb8dsp_midi_interrupt(chip);
541da177e4SLinus Torvalds 	}
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
snd_sb8_match(struct device * pdev,unsigned int dev)571bff292eSBill Pemberton static int snd_sb8_match(struct device *pdev, unsigned int dev)
581da177e4SLinus Torvalds {
595e24c1c1STakashi Iwai 	if (!enable[dev])
605e24c1c1STakashi Iwai 		return 0;
615e24c1c1STakashi Iwai 	if (irq[dev] == SNDRV_AUTO_IRQ) {
620418ff0cSTakashi Iwai 		dev_err(pdev, "please specify irq\n");
635e24c1c1STakashi Iwai 		return 0;
645e24c1c1STakashi Iwai 	}
655e24c1c1STakashi Iwai 	if (dma8[dev] == SNDRV_AUTO_DMA) {
660418ff0cSTakashi Iwai 		dev_err(pdev, "please specify dma8\n");
675e24c1c1STakashi Iwai 		return 0;
685e24c1c1STakashi Iwai 	}
695e24c1c1STakashi Iwai 	return 1;
705e24c1c1STakashi Iwai }
715e24c1c1STakashi Iwai 
snd_sb8_probe(struct device * pdev,unsigned int dev)721bff292eSBill Pemberton static int snd_sb8_probe(struct device *pdev, unsigned int dev)
735e24c1c1STakashi Iwai {
74029d64b0STakashi Iwai 	struct snd_sb *chip;
75029d64b0STakashi Iwai 	struct snd_card *card;
761da177e4SLinus Torvalds 	struct snd_sb8 *acard;
77029d64b0STakashi Iwai 	struct snd_opl3 *opl3;
781da177e4SLinus Torvalds 	int err;
791da177e4SLinus Torvalds 
80*5eab6cb0STakashi Iwai 	err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
81c95eadd2STakashi Iwai 				sizeof(struct snd_sb8), &card);
82c95eadd2STakashi Iwai 	if (err < 0)
83c95eadd2STakashi Iwai 		return err;
8467be4458STakashi Iwai 	acard = card->private_data;
851da177e4SLinus Torvalds 
86a28591f6SAtul Gopinathan 	/*
87a28591f6SAtul Gopinathan 	 * Block the 0x388 port to avoid PnP conflicts.
88a28591f6SAtul Gopinathan 	 * No need to check this value after request_region,
89a28591f6SAtul Gopinathan 	 * as we never do anything with it.
90a28591f6SAtul Gopinathan 	 */
91*5eab6cb0STakashi Iwai 	acard->fm_res = devm_request_region(card->dev, 0x388, 4,
92*5eab6cb0STakashi Iwai 					    "SoundBlaster FM");
931da177e4SLinus Torvalds 
9467be4458STakashi Iwai 	if (port[dev] != SNDRV_AUTO_PORT) {
9510dc8ad5STakashi Iwai 		err = snd_sbdsp_create(card, port[dev], irq[dev],
9610dc8ad5STakashi Iwai 				       snd_sb8_interrupt, dma8[dev],
9710dc8ad5STakashi Iwai 				       -1, SB_HW_AUTO, &chip);
9810dc8ad5STakashi Iwai 		if (err < 0)
99*5eab6cb0STakashi Iwai 			return err;
10067be4458STakashi Iwai 	} else {
10167be4458STakashi Iwai 		/* auto-probe legacy ports */
1022a076d0aSTakashi Iwai 		static const unsigned long possible_ports[] = {
10367be4458STakashi Iwai 			0x220, 0x240, 0x260,
10467be4458STakashi Iwai 		};
10567be4458STakashi Iwai 		int i;
10667be4458STakashi Iwai 		for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
10767be4458STakashi Iwai 			err = snd_sbdsp_create(card, possible_ports[i],
10867be4458STakashi Iwai 					       irq[dev],
10967be4458STakashi Iwai 					       snd_sb8_interrupt,
11067be4458STakashi Iwai 					       dma8[dev],
11167be4458STakashi Iwai 					       -1,
11267be4458STakashi Iwai 					       SB_HW_AUTO,
11367be4458STakashi Iwai 					       &chip);
11467be4458STakashi Iwai 			if (err >= 0) {
11567be4458STakashi Iwai 				port[dev] = possible_ports[i];
11667be4458STakashi Iwai 				break;
11767be4458STakashi Iwai 			}
11867be4458STakashi Iwai 		}
119*5eab6cb0STakashi Iwai 		if (i >= ARRAY_SIZE(possible_ports))
120*5eab6cb0STakashi Iwai 			return -EINVAL;
12142a73df4STakashi Iwai 	}
12267be4458STakashi Iwai 	acard->chip = chip;
12343bcd973STakashi Iwai 
1241da177e4SLinus Torvalds 	if (chip->hardware >= SB_HW_16) {
1251da177e4SLinus Torvalds 		if (chip->hardware == SB_HW_ALS100)
12643bcd973STakashi Iwai 			snd_printk(KERN_WARNING "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
1271da177e4SLinus Torvalds 				    port[dev]);
1281da177e4SLinus Torvalds 		else
12943bcd973STakashi Iwai 			snd_printk(KERN_WARNING "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
1301da177e4SLinus Torvalds 				   port[dev]);
131*5eab6cb0STakashi Iwai 		return -ENODEV;
1321da177e4SLinus Torvalds 	}
1331da177e4SLinus Torvalds 
13410dc8ad5STakashi Iwai 	err = snd_sb8dsp_pcm(chip, 0);
13510dc8ad5STakashi Iwai 	if (err < 0)
136*5eab6cb0STakashi Iwai 		return err;
13743bcd973STakashi Iwai 
13810dc8ad5STakashi Iwai 	err = snd_sbmixer_new(chip);
13910dc8ad5STakashi Iwai 	if (err < 0)
140*5eab6cb0STakashi Iwai 		return err;
14143bcd973STakashi Iwai 
1421da177e4SLinus Torvalds 	if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
14310dc8ad5STakashi Iwai 		err = snd_opl3_create(card, chip->port + 8, 0,
14410dc8ad5STakashi Iwai 				      OPL3_HW_AUTO, 1, &opl3);
14510dc8ad5STakashi Iwai 		if (err < 0)
14643bcd973STakashi Iwai 			snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx\n", chip->port + 8);
1471da177e4SLinus Torvalds 	} else {
14810dc8ad5STakashi Iwai 		err = snd_opl3_create(card, chip->port, chip->port + 2,
14910dc8ad5STakashi Iwai 				      OPL3_HW_AUTO, 1, &opl3);
15010dc8ad5STakashi Iwai 		if (err < 0) {
15143bcd973STakashi Iwai 			snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx-0x%lx\n",
1521da177e4SLinus Torvalds 				   chip->port, chip->port + 2);
1531da177e4SLinus Torvalds 		}
1541da177e4SLinus Torvalds 	}
1551da177e4SLinus Torvalds 	if (err >= 0) {
15610dc8ad5STakashi Iwai 		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
15710dc8ad5STakashi Iwai 		if (err < 0)
158*5eab6cb0STakashi Iwai 			return err;
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 
16110dc8ad5STakashi Iwai 	err = snd_sb8dsp_midi(chip, 0);
16210dc8ad5STakashi Iwai 	if (err < 0)
163*5eab6cb0STakashi Iwai 		return err;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
1661da177e4SLinus Torvalds 	strcpy(card->shortname, chip->name);
1671da177e4SLinus Torvalds 	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
1681da177e4SLinus Torvalds 		chip->name,
1691da177e4SLinus Torvalds 		chip->port,
1701da177e4SLinus Torvalds 		irq[dev], dma8[dev]);
17143bcd973STakashi Iwai 
17210dc8ad5STakashi Iwai 	err = snd_card_register(card);
17310dc8ad5STakashi Iwai 	if (err < 0)
174*5eab6cb0STakashi Iwai 		return err;
17543bcd973STakashi Iwai 
1765e24c1c1STakashi Iwai 	dev_set_drvdata(pdev, card);
1771da177e4SLinus Torvalds 	return 0;
17867be4458STakashi Iwai }
1791da177e4SLinus Torvalds 
18067be4458STakashi Iwai #ifdef CONFIG_PM
snd_sb8_suspend(struct device * dev,unsigned int n,pm_message_t state)1815e24c1c1STakashi Iwai static int snd_sb8_suspend(struct device *dev, unsigned int n,
1825e24c1c1STakashi Iwai 			   pm_message_t state)
18367be4458STakashi Iwai {
1845e24c1c1STakashi Iwai 	struct snd_card *card = dev_get_drvdata(dev);
18567be4458STakashi Iwai 	struct snd_sb8 *acard = card->private_data;
18667be4458STakashi Iwai 	struct snd_sb *chip = acard->chip;
18767be4458STakashi Iwai 
18867be4458STakashi Iwai 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
18967be4458STakashi Iwai 	snd_sbmixer_suspend(chip);
19067be4458STakashi Iwai 	return 0;
1911da177e4SLinus Torvalds }
19267be4458STakashi Iwai 
snd_sb8_resume(struct device * dev,unsigned int n)1935e24c1c1STakashi Iwai static int snd_sb8_resume(struct device *dev, unsigned int n)
19467be4458STakashi Iwai {
1955e24c1c1STakashi Iwai 	struct snd_card *card = dev_get_drvdata(dev);
19667be4458STakashi Iwai 	struct snd_sb8 *acard = card->private_data;
19767be4458STakashi Iwai 	struct snd_sb *chip = acard->chip;
19867be4458STakashi Iwai 
19967be4458STakashi Iwai 	snd_sbdsp_reset(chip);
20067be4458STakashi Iwai 	snd_sbmixer_resume(chip);
20167be4458STakashi Iwai 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
20267be4458STakashi Iwai 	return 0;
2031da177e4SLinus Torvalds }
20467be4458STakashi Iwai #endif
20567be4458STakashi Iwai 
20683c51c0aSRene Herman #define DEV_NAME "sb8"
20767be4458STakashi Iwai 
2085e24c1c1STakashi Iwai static struct isa_driver snd_sb8_driver = {
2095e24c1c1STakashi Iwai 	.match		= snd_sb8_match,
21067be4458STakashi Iwai 	.probe		= snd_sb8_probe,
21167be4458STakashi Iwai #ifdef CONFIG_PM
21267be4458STakashi Iwai 	.suspend	= snd_sb8_suspend,
21367be4458STakashi Iwai 	.resume		= snd_sb8_resume,
21467be4458STakashi Iwai #endif
21567be4458STakashi Iwai 	.driver		= {
21683c51c0aSRene Herman 		.name	= DEV_NAME
21767be4458STakashi Iwai 	},
21867be4458STakashi Iwai };
2191da177e4SLinus Torvalds 
220bb974b8fSWilliam Breathitt Gray module_isa_driver(snd_sb8_driver, SNDRV_CARDS);
221