18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci    card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
58c2ecf20Sopenharmony_ci    Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci*/
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/time.h>
118c2ecf20Sopenharmony_ci#include <linux/wait.h>
128c2ecf20Sopenharmony_ci#include <linux/pnp.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <sound/core.h>
158c2ecf20Sopenharmony_ci#include <sound/initval.h>
168c2ecf20Sopenharmony_ci#include <sound/ad1816a.h>
178c2ecf20Sopenharmony_ci#include <sound/mpu401.h>
188c2ecf20Sopenharmony_ci#include <sound/opl3.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PFX "ad1816a: "
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AD1816A, AD1815");
248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
258c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D},"
268c2ecf20Sopenharmony_ci		"{Analog Devices,AD1815},"
278c2ecf20Sopenharmony_ci		"{Analog Devices,AD1816A},"
288c2ecf20Sopenharmony_ci		"{TerraTec,Base 64},"
298c2ecf20Sopenharmony_ci		"{TerraTec,AudioSystem EWS64S},"
308c2ecf20Sopenharmony_ci		"{Aztech/Newcom SC-16 3D},"
318c2ecf20Sopenharmony_ci		"{Shark Predator ISA}}");
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 1-MAX */
348c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
358c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;	/* Enable this card */
368c2ecf20Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
378c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
388c2ecf20Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
398c2ecf20Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
408c2ecf20Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
418c2ecf20Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
428c2ecf20Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
438c2ecf20Sopenharmony_cistatic int clockfreq[SNDRV_CARDS];
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ad1816a based soundcard.");
478c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ad1816a based soundcard.");
498c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
518c2ecf20Sopenharmony_cimodule_param_array(clockfreq, int, NULL, 0444);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
558c2ecf20Sopenharmony_ci	/* Analog Devices AD1815 */
568c2ecf20Sopenharmony_ci	{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
578c2ecf20Sopenharmony_ci	/* Analog Devices AD1816? */
588c2ecf20Sopenharmony_ci	{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
598c2ecf20Sopenharmony_ci	/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
608c2ecf20Sopenharmony_ci	{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
618c2ecf20Sopenharmony_ci	/* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
628c2ecf20Sopenharmony_ci	{ .id = "AZT1022", .devs = { { .id = "AZT1018" }, { .id = "AZT2002" } } },
638c2ecf20Sopenharmony_ci	/* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */
648c2ecf20Sopenharmony_ci	{ .id = "LWC1061", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
658c2ecf20Sopenharmony_ci	/* Highscreen Sound-Boostar 16 3D */
668c2ecf20Sopenharmony_ci	{ .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
678c2ecf20Sopenharmony_ci	/* Shark Predator ISA - added by Ken Arromdee */
688c2ecf20Sopenharmony_ci	{ .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
698c2ecf20Sopenharmony_ci	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
708c2ecf20Sopenharmony_ci	{ .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
718c2ecf20Sopenharmony_ci	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
728c2ecf20Sopenharmony_ci	{ .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } },
738c2ecf20Sopenharmony_ci	/* Analog Devices AD1816A - Terratec Base 64 */
748c2ecf20Sopenharmony_ci	{ .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
758c2ecf20Sopenharmony_ci	/* end */
768c2ecf20Sopenharmony_ci	{ .id = "" }
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define	DRIVER_NAME	"snd-card-ad1816a"
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
868c2ecf20Sopenharmony_ci				const struct pnp_card_device_id *id)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct pnp_dev *pdev;
898c2ecf20Sopenharmony_ci	int err;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	pdev = pnp_request_card_device(card, id->devs[0].id, NULL);
928c2ecf20Sopenharmony_ci	if (pdev == NULL)
938c2ecf20Sopenharmony_ci		return -EBUSY;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	err = pnp_activate_dev(pdev);
968c2ecf20Sopenharmony_ci	if (err < 0) {
978c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
988c2ecf20Sopenharmony_ci		return -EBUSY;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	port[dev] = pnp_port_start(pdev, 2);
1028c2ecf20Sopenharmony_ci	fm_port[dev] = pnp_port_start(pdev, 1);
1038c2ecf20Sopenharmony_ci	dma1[dev] = pnp_dma(pdev, 0);
1048c2ecf20Sopenharmony_ci	dma2[dev] = pnp_dma(pdev, 1);
1058c2ecf20Sopenharmony_ci	irq[dev] = pnp_irq(pdev, 0);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	pdev = pnp_request_card_device(card, id->devs[1].id, NULL);
1088c2ecf20Sopenharmony_ci	if (pdev == NULL) {
1098c2ecf20Sopenharmony_ci		mpu_port[dev] = -1;
1108c2ecf20Sopenharmony_ci		snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
1118c2ecf20Sopenharmony_ci		return 0;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	err = pnp_activate_dev(pdev);
1158c2ecf20Sopenharmony_ci	if (err < 0) {
1168c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
1178c2ecf20Sopenharmony_ci		mpu_port[dev] = -1;
1188c2ecf20Sopenharmony_ci	} else {
1198c2ecf20Sopenharmony_ci		mpu_port[dev] = pnp_port_start(pdev, 0);
1208c2ecf20Sopenharmony_ci		mpu_irq[dev] = pnp_irq(pdev, 0);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
1278c2ecf20Sopenharmony_ci				  const struct pnp_card_device_id *pid)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int error;
1308c2ecf20Sopenharmony_ci	struct snd_card *card;
1318c2ecf20Sopenharmony_ci	struct snd_ad1816a *chip;
1328c2ecf20Sopenharmony_ci	struct snd_opl3 *opl3;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	error = snd_card_new(&pcard->card->dev,
1358c2ecf20Sopenharmony_ci			     index[dev], id[dev], THIS_MODULE,
1368c2ecf20Sopenharmony_ci			     sizeof(struct snd_ad1816a), &card);
1378c2ecf20Sopenharmony_ci	if (error < 0)
1388c2ecf20Sopenharmony_ci		return error;
1398c2ecf20Sopenharmony_ci	chip = card->private_data;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if ((error = snd_card_ad1816a_pnp(dev, pcard, pid))) {
1428c2ecf20Sopenharmony_ci		snd_card_free(card);
1438c2ecf20Sopenharmony_ci		return error;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if ((error = snd_ad1816a_create(card, port[dev],
1478c2ecf20Sopenharmony_ci					irq[dev],
1488c2ecf20Sopenharmony_ci					dma1[dev],
1498c2ecf20Sopenharmony_ci					dma2[dev],
1508c2ecf20Sopenharmony_ci					chip)) < 0) {
1518c2ecf20Sopenharmony_ci		snd_card_free(card);
1528c2ecf20Sopenharmony_ci		return error;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000)
1558c2ecf20Sopenharmony_ci		chip->clock_freq = clockfreq[dev];
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	strcpy(card->driver, "AD1816A");
1588c2ecf20Sopenharmony_ci	strcpy(card->shortname, "ADI SoundPort AD1816A");
1598c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d",
1608c2ecf20Sopenharmony_ci		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if ((error = snd_ad1816a_pcm(chip, 0)) < 0) {
1638c2ecf20Sopenharmony_ci		snd_card_free(card);
1648c2ecf20Sopenharmony_ci		return error;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if ((error = snd_ad1816a_mixer(chip)) < 0) {
1688c2ecf20Sopenharmony_ci		snd_card_free(card);
1698c2ecf20Sopenharmony_ci		return error;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	error = snd_ad1816a_timer(chip, 0);
1738c2ecf20Sopenharmony_ci	if (error < 0) {
1748c2ecf20Sopenharmony_ci		snd_card_free(card);
1758c2ecf20Sopenharmony_ci		return error;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (mpu_port[dev] > 0) {
1798c2ecf20Sopenharmony_ci		if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
1808c2ecf20Sopenharmony_ci					mpu_port[dev], 0, mpu_irq[dev],
1818c2ecf20Sopenharmony_ci					NULL) < 0)
1828c2ecf20Sopenharmony_ci			printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (fm_port[dev] > 0) {
1868c2ecf20Sopenharmony_ci		if (snd_opl3_create(card,
1878c2ecf20Sopenharmony_ci				    fm_port[dev], fm_port[dev] + 2,
1888c2ecf20Sopenharmony_ci				    OPL3_HW_AUTO, 0, &opl3) < 0) {
1898c2ecf20Sopenharmony_ci			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
1908c2ecf20Sopenharmony_ci		} else {
1918c2ecf20Sopenharmony_ci			error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
1928c2ecf20Sopenharmony_ci			if (error < 0) {
1938c2ecf20Sopenharmony_ci				snd_card_free(card);
1948c2ecf20Sopenharmony_ci				return error;
1958c2ecf20Sopenharmony_ci			}
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if ((error = snd_card_register(card)) < 0) {
2008c2ecf20Sopenharmony_ci		snd_card_free(card);
2018c2ecf20Sopenharmony_ci		return error;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
2048c2ecf20Sopenharmony_ci	return 0;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic unsigned int ad1816a_devices;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int snd_ad1816a_pnp_detect(struct pnp_card_link *card,
2108c2ecf20Sopenharmony_ci				  const struct pnp_card_device_id *id)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	static int dev;
2138c2ecf20Sopenharmony_ci	int res;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	for ( ; dev < SNDRV_CARDS; dev++) {
2168c2ecf20Sopenharmony_ci		if (!enable[dev])
2178c2ecf20Sopenharmony_ci			continue;
2188c2ecf20Sopenharmony_ci		res = snd_card_ad1816a_probe(dev, card, id);
2198c2ecf20Sopenharmony_ci		if (res < 0)
2208c2ecf20Sopenharmony_ci			return res;
2218c2ecf20Sopenharmony_ci		dev++;
2228c2ecf20Sopenharmony_ci		ad1816a_devices++;
2238c2ecf20Sopenharmony_ci		return 0;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci        return -ENODEV;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void snd_ad1816a_pnp_remove(struct pnp_card_link *pcard)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	snd_card_free(pnp_get_card_drvdata(pcard));
2318c2ecf20Sopenharmony_ci	pnp_set_card_drvdata(pcard, NULL);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
2358c2ecf20Sopenharmony_cistatic int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
2368c2ecf20Sopenharmony_ci				   pm_message_t state)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct snd_card *card = pnp_get_card_drvdata(pcard);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
2418c2ecf20Sopenharmony_ci	snd_ad1816a_suspend(card->private_data);
2428c2ecf20Sopenharmony_ci	return 0;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct snd_card *card = pnp_get_card_drvdata(pcard);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	snd_ad1816a_resume(card->private_data);
2508c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2518c2ecf20Sopenharmony_ci	return 0;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci#endif
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic struct pnp_card_driver ad1816a_pnpc_driver = {
2568c2ecf20Sopenharmony_ci	.flags		= PNP_DRIVER_RES_DISABLE,
2578c2ecf20Sopenharmony_ci	.name		= "ad1816a",
2588c2ecf20Sopenharmony_ci	.id_table	= snd_ad1816a_pnpids,
2598c2ecf20Sopenharmony_ci	.probe		= snd_ad1816a_pnp_detect,
2608c2ecf20Sopenharmony_ci	.remove		= snd_ad1816a_pnp_remove,
2618c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
2628c2ecf20Sopenharmony_ci	.suspend	= snd_ad1816a_pnp_suspend,
2638c2ecf20Sopenharmony_ci	.resume		= snd_ad1816a_pnp_resume,
2648c2ecf20Sopenharmony_ci#endif
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int __init alsa_card_ad1816a_init(void)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	int err;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	err = pnp_register_card_driver(&ad1816a_pnpc_driver);
2728c2ecf20Sopenharmony_ci	if (err)
2738c2ecf20Sopenharmony_ci		return err;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (!ad1816a_devices) {
2768c2ecf20Sopenharmony_ci		pnp_unregister_card_driver(&ad1816a_pnpc_driver);
2778c2ecf20Sopenharmony_ci#ifdef MODULE
2788c2ecf20Sopenharmony_ci		printk(KERN_ERR "no AD1816A based soundcards found.\n");
2798c2ecf20Sopenharmony_ci#endif	/* MODULE */
2808c2ecf20Sopenharmony_ci		return -ENODEV;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void __exit alsa_card_ad1816a_exit(void)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	pnp_unregister_card_driver(&ad1816a_pnpc_driver);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cimodule_init(alsa_card_ad1816a_init)
2918c2ecf20Sopenharmony_cimodule_exit(alsa_card_ad1816a_exit)
292