xref: /kernel/linux/linux-6.6/sound/isa/azt2320.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci    card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
462306a36Sopenharmony_ci    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci*/
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci    This driver should provide support for most Aztech AZT2320 based cards.
1062306a36Sopenharmony_ci    Several AZT2316 chips are also supported/tested, but autoprobe doesn't
1162306a36Sopenharmony_ci    work: all module option have to be set.
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci    No docs available for us at Aztech headquarters !!!   Unbelievable ...
1462306a36Sopenharmony_ci    No other help obtained.
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci    Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS
1762306a36Sopenharmony_ci    activation method (full-duplex audio!).
1862306a36Sopenharmony_ci*/
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/time.h>
2462306a36Sopenharmony_ci#include <linux/wait.h>
2562306a36Sopenharmony_ci#include <linux/pnp.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <sound/core.h>
2862306a36Sopenharmony_ci#include <sound/initval.h>
2962306a36Sopenharmony_ci#include <sound/wss.h>
3062306a36Sopenharmony_ci#include <sound/mpu401.h>
3162306a36Sopenharmony_ci#include <sound/opl3.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define PFX "azt2320: "
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ciMODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
3662306a36Sopenharmony_ciMODULE_DESCRIPTION("Aztech Systems AZT2320");
3762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
4062306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
4162306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
4262306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
4362306a36Sopenharmony_cistatic long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
4462306a36Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
4562306a36Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
4662306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
4762306a36Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
4862306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
4962306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
5262306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
5362306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
5462306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
5562306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
5662306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct snd_card_azt2320 {
5962306a36Sopenharmony_ci	int dev_no;
6062306a36Sopenharmony_ci	struct pnp_dev *dev;
6162306a36Sopenharmony_ci	struct pnp_dev *devmpu;
6262306a36Sopenharmony_ci	struct snd_wss *chip;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_azt2320_pnpids[] = {
6662306a36Sopenharmony_ci	/* PRO16V */
6762306a36Sopenharmony_ci	{ .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
6862306a36Sopenharmony_ci	/* Aztech Sound Galaxy 16 */
6962306a36Sopenharmony_ci	{ .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
7062306a36Sopenharmony_ci	/* Packard Bell Sound III 336 AM/SP */
7162306a36Sopenharmony_ci	{ .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
7262306a36Sopenharmony_ci	/* AT3300 */
7362306a36Sopenharmony_ci	{ .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
7462306a36Sopenharmony_ci	/* --- */
7562306a36Sopenharmony_ci	{ .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
7662306a36Sopenharmony_ci	/* --- */
7762306a36Sopenharmony_ci	{ .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
7862306a36Sopenharmony_ci	{ .id = "" }	/* end */
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define	DRIVER_NAME	"snd-card-azt2320"
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
8662306a36Sopenharmony_ci				struct pnp_card_link *card,
8762306a36Sopenharmony_ci				const struct pnp_card_device_id *id)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct pnp_dev *pdev;
9062306a36Sopenharmony_ci	int err;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
9362306a36Sopenharmony_ci	if (acard->dev == NULL)
9462306a36Sopenharmony_ci		return -ENODEV;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	pdev = acard->dev;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	err = pnp_activate_dev(pdev);
10162306a36Sopenharmony_ci	if (err < 0) {
10262306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
10362306a36Sopenharmony_ci		return err;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	port[dev] = pnp_port_start(pdev, 0);
10662306a36Sopenharmony_ci	fm_port[dev] = pnp_port_start(pdev, 1);
10762306a36Sopenharmony_ci	wss_port[dev] = pnp_port_start(pdev, 2);
10862306a36Sopenharmony_ci	dma1[dev] = pnp_dma(pdev, 0);
10962306a36Sopenharmony_ci	dma2[dev] = pnp_dma(pdev, 1);
11062306a36Sopenharmony_ci	irq[dev] = pnp_irq(pdev, 0);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	pdev = acard->devmpu;
11362306a36Sopenharmony_ci	if (pdev != NULL) {
11462306a36Sopenharmony_ci		err = pnp_activate_dev(pdev);
11562306a36Sopenharmony_ci		if (err < 0)
11662306a36Sopenharmony_ci			goto __mpu_error;
11762306a36Sopenharmony_ci		mpu_port[dev] = pnp_port_start(pdev, 0);
11862306a36Sopenharmony_ci		mpu_irq[dev] = pnp_irq(pdev, 0);
11962306a36Sopenharmony_ci	} else {
12062306a36Sopenharmony_ci	     __mpu_error:
12162306a36Sopenharmony_ci	     	if (pdev) {
12262306a36Sopenharmony_ci		     	pnp_release_card_device(pdev);
12362306a36Sopenharmony_ci	     		snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
12462306a36Sopenharmony_ci	     	}
12562306a36Sopenharmony_ci	     	acard->devmpu = NULL;
12662306a36Sopenharmony_ci	     	mpu_port[dev] = -1;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* same of snd_sbdsp_command by Jaroslav Kysela */
13362306a36Sopenharmony_cistatic int snd_card_azt2320_command(unsigned long port, unsigned char val)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int i;
13662306a36Sopenharmony_ci	unsigned long limit;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	limit = jiffies + HZ / 10;
13962306a36Sopenharmony_ci	for (i = 50000; i && time_after(limit, jiffies); i--)
14062306a36Sopenharmony_ci		if (!(inb(port + 0x0c) & 0x80)) {
14162306a36Sopenharmony_ci			outb(val, port + 0x0c);
14262306a36Sopenharmony_ci			return 0;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	return -EBUSY;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int snd_card_azt2320_enable_wss(unsigned long port)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	int error;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	error = snd_card_azt2320_command(port, 0x09);
15262306a36Sopenharmony_ci	if (error)
15362306a36Sopenharmony_ci		return error;
15462306a36Sopenharmony_ci	error = snd_card_azt2320_command(port, 0x00);
15562306a36Sopenharmony_ci	if (error)
15662306a36Sopenharmony_ci		return error;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	mdelay(5);
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int snd_card_azt2320_probe(int dev,
16362306a36Sopenharmony_ci				  struct pnp_card_link *pcard,
16462306a36Sopenharmony_ci				  const struct pnp_card_device_id *pid)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	int error;
16762306a36Sopenharmony_ci	struct snd_card *card;
16862306a36Sopenharmony_ci	struct snd_card_azt2320 *acard;
16962306a36Sopenharmony_ci	struct snd_wss *chip;
17062306a36Sopenharmony_ci	struct snd_opl3 *opl3;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	error = snd_devm_card_new(&pcard->card->dev,
17362306a36Sopenharmony_ci				  index[dev], id[dev], THIS_MODULE,
17462306a36Sopenharmony_ci				  sizeof(struct snd_card_azt2320), &card);
17562306a36Sopenharmony_ci	if (error < 0)
17662306a36Sopenharmony_ci		return error;
17762306a36Sopenharmony_ci	acard = card->private_data;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	error = snd_card_azt2320_pnp(dev, acard, pcard, pid);
18062306a36Sopenharmony_ci	if (error)
18162306a36Sopenharmony_ci		return error;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	error = snd_card_azt2320_enable_wss(port[dev]);
18462306a36Sopenharmony_ci	if (error)
18562306a36Sopenharmony_ci		return error;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	error = snd_wss_create(card, wss_port[dev], -1,
18862306a36Sopenharmony_ci			       irq[dev],
18962306a36Sopenharmony_ci			       dma1[dev], dma2[dev],
19062306a36Sopenharmony_ci			       WSS_HW_DETECT, 0, &chip);
19162306a36Sopenharmony_ci	if (error < 0)
19262306a36Sopenharmony_ci		return error;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	strcpy(card->driver, "AZT2320");
19562306a36Sopenharmony_ci	strcpy(card->shortname, "Aztech AZT2320");
19662306a36Sopenharmony_ci	sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
19762306a36Sopenharmony_ci		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	error = snd_wss_pcm(chip, 0);
20062306a36Sopenharmony_ci	if (error < 0)
20162306a36Sopenharmony_ci		return error;
20262306a36Sopenharmony_ci	error = snd_wss_mixer(chip);
20362306a36Sopenharmony_ci	if (error < 0)
20462306a36Sopenharmony_ci		return error;
20562306a36Sopenharmony_ci	error = snd_wss_timer(chip, 0);
20662306a36Sopenharmony_ci	if (error < 0)
20762306a36Sopenharmony_ci		return error;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
21062306a36Sopenharmony_ci		if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
21162306a36Sopenharmony_ci				mpu_port[dev], 0,
21262306a36Sopenharmony_ci				mpu_irq[dev], NULL) < 0)
21362306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
21762306a36Sopenharmony_ci		if (snd_opl3_create(card,
21862306a36Sopenharmony_ci				    fm_port[dev], fm_port[dev] + 2,
21962306a36Sopenharmony_ci				    OPL3_HW_AUTO, 0, &opl3) < 0) {
22062306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
22162306a36Sopenharmony_ci				   fm_port[dev], fm_port[dev] + 2);
22262306a36Sopenharmony_ci		} else {
22362306a36Sopenharmony_ci			error = snd_opl3_timer_new(opl3, 1, 2);
22462306a36Sopenharmony_ci			if (error < 0)
22562306a36Sopenharmony_ci				return error;
22662306a36Sopenharmony_ci			error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
22762306a36Sopenharmony_ci			if (error < 0)
22862306a36Sopenharmony_ci				return error;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	error = snd_card_register(card);
23362306a36Sopenharmony_ci	if (error < 0)
23462306a36Sopenharmony_ci		return error;
23562306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
23662306a36Sopenharmony_ci	return 0;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic unsigned int azt2320_devices;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int snd_azt2320_pnp_detect(struct pnp_card_link *card,
24262306a36Sopenharmony_ci				  const struct pnp_card_device_id *id)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	static int dev;
24562306a36Sopenharmony_ci	int res;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for ( ; dev < SNDRV_CARDS; dev++) {
24862306a36Sopenharmony_ci		if (!enable[dev])
24962306a36Sopenharmony_ci			continue;
25062306a36Sopenharmony_ci		res = snd_card_azt2320_probe(dev, card, id);
25162306a36Sopenharmony_ci		if (res < 0)
25262306a36Sopenharmony_ci			return res;
25362306a36Sopenharmony_ci		dev++;
25462306a36Sopenharmony_ci		azt2320_devices++;
25562306a36Sopenharmony_ci		return 0;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci        return -ENODEV;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci#ifdef CONFIG_PM
26162306a36Sopenharmony_cistatic int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct snd_card *card = pnp_get_card_drvdata(pcard);
26462306a36Sopenharmony_ci	struct snd_card_azt2320 *acard = card->private_data;
26562306a36Sopenharmony_ci	struct snd_wss *chip = acard->chip;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
26862306a36Sopenharmony_ci	chip->suspend(chip);
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct snd_card *card = pnp_get_card_drvdata(pcard);
27562306a36Sopenharmony_ci	struct snd_card_azt2320 *acard = card->private_data;
27662306a36Sopenharmony_ci	struct snd_wss *chip = acard->chip;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	chip->resume(chip);
27962306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci#endif
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic struct pnp_card_driver azt2320_pnpc_driver = {
28562306a36Sopenharmony_ci	.flags          = PNP_DRIVER_RES_DISABLE,
28662306a36Sopenharmony_ci	.name           = "azt2320",
28762306a36Sopenharmony_ci	.id_table       = snd_azt2320_pnpids,
28862306a36Sopenharmony_ci	.probe          = snd_azt2320_pnp_detect,
28962306a36Sopenharmony_ci#ifdef CONFIG_PM
29062306a36Sopenharmony_ci	.suspend	= snd_azt2320_pnp_suspend,
29162306a36Sopenharmony_ci	.resume		= snd_azt2320_pnp_resume,
29262306a36Sopenharmony_ci#endif
29362306a36Sopenharmony_ci};
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int __init alsa_card_azt2320_init(void)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int err;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	err = pnp_register_card_driver(&azt2320_pnpc_driver);
30062306a36Sopenharmony_ci	if (err)
30162306a36Sopenharmony_ci		return err;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (!azt2320_devices) {
30462306a36Sopenharmony_ci		pnp_unregister_card_driver(&azt2320_pnpc_driver);
30562306a36Sopenharmony_ci#ifdef MODULE
30662306a36Sopenharmony_ci		snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
30762306a36Sopenharmony_ci#endif
30862306a36Sopenharmony_ci		return -ENODEV;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	return 0;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void __exit alsa_card_azt2320_exit(void)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	pnp_unregister_card_driver(&azt2320_pnpc_driver);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cimodule_init(alsa_card_azt2320_init)
31962306a36Sopenharmony_cimodule_exit(alsa_card_azt2320_exit)
320