162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci    card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
562306a36Sopenharmony_ci    Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci*/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/time.h>
1162306a36Sopenharmony_ci#include <linux/wait.h>
1262306a36Sopenharmony_ci#include <linux/pnp.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <sound/core.h>
1562306a36Sopenharmony_ci#include <sound/initval.h>
1662306a36Sopenharmony_ci#include <sound/ad1816a.h>
1762306a36Sopenharmony_ci#include <sound/mpu401.h>
1862306a36Sopenharmony_ci#include <sound/opl3.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define PFX "ad1816a: "
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciMODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
2362306a36Sopenharmony_ciMODULE_DESCRIPTION("AD1816A, AD1815");
2462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 1-MAX */
2762306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
2862306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;	/* Enable this card */
2962306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
3062306a36Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
3162306a36Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
3262306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
3362306a36Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
3462306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
3562306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
3662306a36Sopenharmony_cistatic int clockfreq[SNDRV_CARDS];
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
3962306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ad1816a based soundcard.");
4062306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ad1816a based soundcard.");
4262306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
4362306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
4462306a36Sopenharmony_cimodule_param_array(clockfreq, int, NULL, 0444);
4562306a36Sopenharmony_ciMODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
4862306a36Sopenharmony_ci	/* Analog Devices AD1815 */
4962306a36Sopenharmony_ci	{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
5062306a36Sopenharmony_ci	/* Analog Devices AD1816? */
5162306a36Sopenharmony_ci	{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
5262306a36Sopenharmony_ci	/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
5362306a36Sopenharmony_ci	{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
5462306a36Sopenharmony_ci	/* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
5562306a36Sopenharmony_ci	{ .id = "AZT1022", .devs = { { .id = "AZT1018" }, { .id = "AZT2002" } } },
5662306a36Sopenharmony_ci	/* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */
5762306a36Sopenharmony_ci	{ .id = "LWC1061", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
5862306a36Sopenharmony_ci	/* Highscreen Sound-Boostar 16 3D */
5962306a36Sopenharmony_ci	{ .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
6062306a36Sopenharmony_ci	/* Shark Predator ISA - added by Ken Arromdee */
6162306a36Sopenharmony_ci	{ .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
6262306a36Sopenharmony_ci	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
6362306a36Sopenharmony_ci	{ .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
6462306a36Sopenharmony_ci	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
6562306a36Sopenharmony_ci	{ .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } },
6662306a36Sopenharmony_ci	/* Analog Devices AD1816A - Terratec Base 64 */
6762306a36Sopenharmony_ci	{ .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
6862306a36Sopenharmony_ci	/* end */
6962306a36Sopenharmony_ci	{ .id = "" }
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define	DRIVER_NAME	"snd-card-ad1816a"
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
7962306a36Sopenharmony_ci				const struct pnp_card_device_id *id)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct pnp_dev *pdev;
8262306a36Sopenharmony_ci	int err;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	pdev = pnp_request_card_device(card, id->devs[0].id, NULL);
8562306a36Sopenharmony_ci	if (pdev == NULL)
8662306a36Sopenharmony_ci		return -EBUSY;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	err = pnp_activate_dev(pdev);
8962306a36Sopenharmony_ci	if (err < 0) {
9062306a36Sopenharmony_ci		printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
9162306a36Sopenharmony_ci		return -EBUSY;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	port[dev] = pnp_port_start(pdev, 2);
9562306a36Sopenharmony_ci	fm_port[dev] = pnp_port_start(pdev, 1);
9662306a36Sopenharmony_ci	dma1[dev] = pnp_dma(pdev, 0);
9762306a36Sopenharmony_ci	dma2[dev] = pnp_dma(pdev, 1);
9862306a36Sopenharmony_ci	irq[dev] = pnp_irq(pdev, 0);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	pdev = pnp_request_card_device(card, id->devs[1].id, NULL);
10162306a36Sopenharmony_ci	if (pdev == NULL) {
10262306a36Sopenharmony_ci		mpu_port[dev] = -1;
10362306a36Sopenharmony_ci		snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
10462306a36Sopenharmony_ci		return 0;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	err = pnp_activate_dev(pdev);
10862306a36Sopenharmony_ci	if (err < 0) {
10962306a36Sopenharmony_ci		printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
11062306a36Sopenharmony_ci		mpu_port[dev] = -1;
11162306a36Sopenharmony_ci	} else {
11262306a36Sopenharmony_ci		mpu_port[dev] = pnp_port_start(pdev, 0);
11362306a36Sopenharmony_ci		mpu_irq[dev] = pnp_irq(pdev, 0);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
12062306a36Sopenharmony_ci				  const struct pnp_card_device_id *pid)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int error;
12362306a36Sopenharmony_ci	struct snd_card *card;
12462306a36Sopenharmony_ci	struct snd_ad1816a *chip;
12562306a36Sopenharmony_ci	struct snd_opl3 *opl3;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	error = snd_devm_card_new(&pcard->card->dev,
12862306a36Sopenharmony_ci				  index[dev], id[dev], THIS_MODULE,
12962306a36Sopenharmony_ci				  sizeof(struct snd_ad1816a), &card);
13062306a36Sopenharmony_ci	if (error < 0)
13162306a36Sopenharmony_ci		return error;
13262306a36Sopenharmony_ci	chip = card->private_data;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	error = snd_card_ad1816a_pnp(dev, pcard, pid);
13562306a36Sopenharmony_ci	if (error)
13662306a36Sopenharmony_ci		return error;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	error = snd_ad1816a_create(card, port[dev],
13962306a36Sopenharmony_ci				   irq[dev],
14062306a36Sopenharmony_ci				   dma1[dev],
14162306a36Sopenharmony_ci				   dma2[dev],
14262306a36Sopenharmony_ci				   chip);
14362306a36Sopenharmony_ci	if (error)
14462306a36Sopenharmony_ci		return error;
14562306a36Sopenharmony_ci	if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000)
14662306a36Sopenharmony_ci		chip->clock_freq = clockfreq[dev];
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	strcpy(card->driver, "AD1816A");
14962306a36Sopenharmony_ci	strcpy(card->shortname, "ADI SoundPort AD1816A");
15062306a36Sopenharmony_ci	sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d",
15162306a36Sopenharmony_ci		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	error = snd_ad1816a_pcm(chip, 0);
15462306a36Sopenharmony_ci	if (error < 0)
15562306a36Sopenharmony_ci		return error;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	error = snd_ad1816a_mixer(chip);
15862306a36Sopenharmony_ci	if (error < 0)
15962306a36Sopenharmony_ci		return error;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	error = snd_ad1816a_timer(chip, 0);
16262306a36Sopenharmony_ci	if (error < 0)
16362306a36Sopenharmony_ci		return error;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (mpu_port[dev] > 0) {
16662306a36Sopenharmony_ci		if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
16762306a36Sopenharmony_ci					mpu_port[dev], 0, mpu_irq[dev],
16862306a36Sopenharmony_ci					NULL) < 0)
16962306a36Sopenharmony_ci			printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (fm_port[dev] > 0) {
17362306a36Sopenharmony_ci		if (snd_opl3_create(card,
17462306a36Sopenharmony_ci				    fm_port[dev], fm_port[dev] + 2,
17562306a36Sopenharmony_ci				    OPL3_HW_AUTO, 0, &opl3) < 0) {
17662306a36Sopenharmony_ci			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
17762306a36Sopenharmony_ci		} else {
17862306a36Sopenharmony_ci			error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
17962306a36Sopenharmony_ci			if (error < 0)
18062306a36Sopenharmony_ci				return error;
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	error = snd_card_register(card);
18562306a36Sopenharmony_ci	if (error < 0)
18662306a36Sopenharmony_ci		return error;
18762306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic unsigned int ad1816a_devices;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int snd_ad1816a_pnp_detect(struct pnp_card_link *card,
19462306a36Sopenharmony_ci				  const struct pnp_card_device_id *id)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	static int dev;
19762306a36Sopenharmony_ci	int res;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	for ( ; dev < SNDRV_CARDS; dev++) {
20062306a36Sopenharmony_ci		if (!enable[dev])
20162306a36Sopenharmony_ci			continue;
20262306a36Sopenharmony_ci		res = snd_card_ad1816a_probe(dev, card, id);
20362306a36Sopenharmony_ci		if (res < 0)
20462306a36Sopenharmony_ci			return res;
20562306a36Sopenharmony_ci		dev++;
20662306a36Sopenharmony_ci		ad1816a_devices++;
20762306a36Sopenharmony_ci		return 0;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci        return -ENODEV;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci#ifdef CONFIG_PM
21362306a36Sopenharmony_cistatic int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
21462306a36Sopenharmony_ci				   pm_message_t state)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct snd_card *card = pnp_get_card_drvdata(pcard);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
21962306a36Sopenharmony_ci	snd_ad1816a_suspend(card->private_data);
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct snd_card *card = pnp_get_card_drvdata(pcard);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	snd_ad1816a_resume(card->private_data);
22862306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci#endif
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic struct pnp_card_driver ad1816a_pnpc_driver = {
23462306a36Sopenharmony_ci	.flags		= PNP_DRIVER_RES_DISABLE,
23562306a36Sopenharmony_ci	.name		= "ad1816a",
23662306a36Sopenharmony_ci	.id_table	= snd_ad1816a_pnpids,
23762306a36Sopenharmony_ci	.probe		= snd_ad1816a_pnp_detect,
23862306a36Sopenharmony_ci#ifdef CONFIG_PM
23962306a36Sopenharmony_ci	.suspend	= snd_ad1816a_pnp_suspend,
24062306a36Sopenharmony_ci	.resume		= snd_ad1816a_pnp_resume,
24162306a36Sopenharmony_ci#endif
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int __init alsa_card_ad1816a_init(void)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	int err;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	err = pnp_register_card_driver(&ad1816a_pnpc_driver);
24962306a36Sopenharmony_ci	if (err)
25062306a36Sopenharmony_ci		return err;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!ad1816a_devices) {
25362306a36Sopenharmony_ci		pnp_unregister_card_driver(&ad1816a_pnpc_driver);
25462306a36Sopenharmony_ci#ifdef MODULE
25562306a36Sopenharmony_ci		printk(KERN_ERR "no AD1816A based soundcards found.\n");
25662306a36Sopenharmony_ci#endif	/* MODULE */
25762306a36Sopenharmony_ci		return -ENODEV;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void __exit alsa_card_ad1816a_exit(void)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	pnp_unregister_card_driver(&ad1816a_pnpc_driver);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cimodule_init(alsa_card_ad1816a_init)
26862306a36Sopenharmony_cimodule_exit(alsa_card_ad1816a_exit)
269