11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds
31da177e4SLinus Torvalds /*
41da177e4SLinus Torvalds card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
51da177e4SLinus Torvalds Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds
91da177e4SLinus Torvalds #include <linux/init.h>
101da177e4SLinus Torvalds #include <linux/time.h>
111da177e4SLinus Torvalds #include <linux/wait.h>
121da177e4SLinus Torvalds #include <linux/pnp.h>
1365a77217SPaul Gortmaker #include <linux/module.h>
141da177e4SLinus Torvalds #include <sound/core.h>
151da177e4SLinus Torvalds #include <sound/initval.h>
161da177e4SLinus Torvalds #include <sound/ad1816a.h>
171da177e4SLinus Torvalds #include <sound/mpu401.h>
181da177e4SLinus Torvalds #include <sound/opl3.h>
191da177e4SLinus Torvalds
201da177e4SLinus Torvalds #define PFX "ad1816a: "
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
231da177e4SLinus Torvalds MODULE_DESCRIPTION("AD1816A, AD1815");
241da177e4SLinus Torvalds MODULE_LICENSE("GPL");
251da177e4SLinus Torvalds
261da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */
271da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
28a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
291da177e4SLinus Torvalds static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
301da177e4SLinus Torvalds static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
311da177e4SLinus Torvalds static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
321da177e4SLinus Torvalds static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
331da177e4SLinus Torvalds static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
341da177e4SLinus Torvalds static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
351da177e4SLinus Torvalds static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
365b8f7f73STakashi Iwai static int clockfreq[SNDRV_CARDS];
371da177e4SLinus Torvalds
381da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444);
391da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard.");
401da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444);
411da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard.");
421da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444);
431da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
445b8f7f73STakashi Iwai module_param_array(clockfreq, int, NULL, 0444);
455b8f7f73STakashi Iwai MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
461da177e4SLinus Torvalds
47287b546cSArvind Yadav static const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
481da177e4SLinus Torvalds /* Analog Devices AD1815 */
491da177e4SLinus Torvalds { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
50aa08ff0fSGeert Uytterhoeven /* Analog Devices AD1816? */
5192bb010cSTakashi Iwai { .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
521da177e4SLinus Torvalds /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
531da177e4SLinus Torvalds { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
541da177e4SLinus Torvalds /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
551da177e4SLinus Torvalds { .id = "AZT1022", .devs = { { .id = "AZT1018" }, { .id = "AZT2002" } } },
561da177e4SLinus Torvalds /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */
571da177e4SLinus Torvalds { .id = "LWC1061", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
581da177e4SLinus Torvalds /* Highscreen Sound-Boostar 16 3D */
591da177e4SLinus Torvalds { .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
601da177e4SLinus Torvalds /* Shark Predator ISA - added by Ken Arromdee */
611da177e4SLinus Torvalds { .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
621da177e4SLinus Torvalds /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
631da177e4SLinus Torvalds { .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
6436463a96SRene Herman /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
6536463a96SRene Herman { .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } },
661da177e4SLinus Torvalds /* Analog Devices AD1816A - Terratec Base 64 */
671da177e4SLinus Torvalds { .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
681da177e4SLinus Torvalds /* end */
691da177e4SLinus Torvalds { .id = "" }
701da177e4SLinus Torvalds };
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids);
731da177e4SLinus Torvalds
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds #define DRIVER_NAME "snd-card-ad1816a"
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds
snd_card_ad1816a_pnp(int dev,struct pnp_card_link * card,const struct pnp_card_device_id * id)781bff292eSBill Pemberton static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
791da177e4SLinus Torvalds const struct pnp_card_device_id *id)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds struct pnp_dev *pdev;
821da177e4SLinus Torvalds int err;
831da177e4SLinus Torvalds
84c86b6b45SOndrej Zary pdev = pnp_request_card_device(card, id->devs[0].id, NULL);
85c86b6b45SOndrej Zary if (pdev == NULL)
861da177e4SLinus Torvalds return -EBUSY;
87109c53f8SRene Herman
881da177e4SLinus Torvalds err = pnp_activate_dev(pdev);
891da177e4SLinus Torvalds if (err < 0) {
901da177e4SLinus Torvalds printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
911da177e4SLinus Torvalds return -EBUSY;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds
941da177e4SLinus Torvalds port[dev] = pnp_port_start(pdev, 2);
951da177e4SLinus Torvalds fm_port[dev] = pnp_port_start(pdev, 1);
961da177e4SLinus Torvalds dma1[dev] = pnp_dma(pdev, 0);
971da177e4SLinus Torvalds dma2[dev] = pnp_dma(pdev, 1);
981da177e4SLinus Torvalds irq[dev] = pnp_irq(pdev, 0);
991da177e4SLinus Torvalds
100c86b6b45SOndrej Zary pdev = pnp_request_card_device(card, id->devs[1].id, NULL);
101c86b6b45SOndrej Zary if (pdev == NULL) {
102c86b6b45SOndrej Zary mpu_port[dev] = -1;
103c86b6b45SOndrej Zary snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
1042944275bSRask Ingemann Lambertsen return 0;
105c86b6b45SOndrej Zary }
1061da177e4SLinus Torvalds
1071da177e4SLinus Torvalds err = pnp_activate_dev(pdev);
1081da177e4SLinus Torvalds if (err < 0) {
1091da177e4SLinus Torvalds printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
1101da177e4SLinus Torvalds mpu_port[dev] = -1;
1111da177e4SLinus Torvalds } else {
1121da177e4SLinus Torvalds mpu_port[dev] = pnp_port_start(pdev, 0);
1131da177e4SLinus Torvalds mpu_irq[dev] = pnp_irq(pdev, 0);
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds
1161da177e4SLinus Torvalds return 0;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds
snd_card_ad1816a_probe(int dev,struct pnp_card_link * pcard,const struct pnp_card_device_id * pid)1191bff292eSBill Pemberton static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
1201da177e4SLinus Torvalds const struct pnp_card_device_id *pid)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds int error;
123cbdd0dd1STakashi Iwai struct snd_card *card;
124cbdd0dd1STakashi Iwai struct snd_ad1816a *chip;
125cbdd0dd1STakashi Iwai struct snd_opl3 *opl3;
1261da177e4SLinus Torvalds
127*d6fb54e8STakashi Iwai error = snd_devm_card_new(&pcard->card->dev,
1284323cc4dSTakashi Iwai index[dev], id[dev], THIS_MODULE,
129c86b6b45SOndrej Zary sizeof(struct snd_ad1816a), &card);
130c95eadd2STakashi Iwai if (error < 0)
131c95eadd2STakashi Iwai return error;
132c86b6b45SOndrej Zary chip = card->private_data;
1331da177e4SLinus Torvalds
1345ab6d660STakashi Iwai error = snd_card_ad1816a_pnp(dev, pcard, pid);
135*d6fb54e8STakashi Iwai if (error)
1361da177e4SLinus Torvalds return error;
1371da177e4SLinus Torvalds
1385ab6d660STakashi Iwai error = snd_ad1816a_create(card, port[dev],
1391da177e4SLinus Torvalds irq[dev],
1401da177e4SLinus Torvalds dma1[dev],
1411da177e4SLinus Torvalds dma2[dev],
1425ab6d660STakashi Iwai chip);
143*d6fb54e8STakashi Iwai if (error)
1441da177e4SLinus Torvalds return error;
1455b8f7f73STakashi Iwai if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000)
1465b8f7f73STakashi Iwai chip->clock_freq = clockfreq[dev];
1471da177e4SLinus Torvalds
1481da177e4SLinus Torvalds strcpy(card->driver, "AD1816A");
1491da177e4SLinus Torvalds strcpy(card->shortname, "ADI SoundPort AD1816A");
1501da177e4SLinus Torvalds sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d",
1511da177e4SLinus Torvalds card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
1521da177e4SLinus Torvalds
1535ab6d660STakashi Iwai error = snd_ad1816a_pcm(chip, 0);
154*d6fb54e8STakashi Iwai if (error < 0)
1551da177e4SLinus Torvalds return error;
1561da177e4SLinus Torvalds
1575ab6d660STakashi Iwai error = snd_ad1816a_mixer(chip);
158*d6fb54e8STakashi Iwai if (error < 0)
1591da177e4SLinus Torvalds return error;
1601da177e4SLinus Torvalds
1617f605418SLars-Peter Clausen error = snd_ad1816a_timer(chip, 0);
162*d6fb54e8STakashi Iwai if (error < 0)
163a17ac45aSKrzysztof Helt return error;
164a17ac45aSKrzysztof Helt
1651da177e4SLinus Torvalds if (mpu_port[dev] > 0) {
1661da177e4SLinus Torvalds if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
167dba8b469SClemens Ladisch mpu_port[dev], 0, mpu_irq[dev],
1681da177e4SLinus Torvalds NULL) < 0)
1691da177e4SLinus Torvalds printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds
1721da177e4SLinus Torvalds if (fm_port[dev] > 0) {
1731da177e4SLinus Torvalds if (snd_opl3_create(card,
1741da177e4SLinus Torvalds fm_port[dev], fm_port[dev] + 2,
1751da177e4SLinus Torvalds OPL3_HW_AUTO, 0, &opl3) < 0) {
1761da177e4SLinus Torvalds printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
1771da177e4SLinus Torvalds } else {
178aa9c293aSKrzysztof Helt error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
179*d6fb54e8STakashi Iwai if (error < 0)
1801da177e4SLinus Torvalds return error;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds
1845ab6d660STakashi Iwai error = snd_card_register(card);
185*d6fb54e8STakashi Iwai if (error < 0)
1861da177e4SLinus Torvalds return error;
1871da177e4SLinus Torvalds pnp_set_card_drvdata(pcard, card);
1881da177e4SLinus Torvalds return 0;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds
1911bff292eSBill Pemberton static unsigned int ad1816a_devices;
1925f53f4e2SBjorn Helgaas
snd_ad1816a_pnp_detect(struct pnp_card_link * card,const struct pnp_card_device_id * id)1931bff292eSBill Pemberton static int snd_ad1816a_pnp_detect(struct pnp_card_link *card,
1941da177e4SLinus Torvalds const struct pnp_card_device_id *id)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds static int dev;
1971da177e4SLinus Torvalds int res;
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds for ( ; dev < SNDRV_CARDS; dev++) {
2001da177e4SLinus Torvalds if (!enable[dev])
2011da177e4SLinus Torvalds continue;
2021da177e4SLinus Torvalds res = snd_card_ad1816a_probe(dev, card, id);
2031da177e4SLinus Torvalds if (res < 0)
2041da177e4SLinus Torvalds return res;
2051da177e4SLinus Torvalds dev++;
2065f53f4e2SBjorn Helgaas ad1816a_devices++;
2071da177e4SLinus Torvalds return 0;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds return -ENODEV;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds
2126f0fa660SOndrej Zary #ifdef CONFIG_PM
snd_ad1816a_pnp_suspend(struct pnp_card_link * pcard,pm_message_t state)2136f0fa660SOndrej Zary static int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
2146f0fa660SOndrej Zary pm_message_t state)
2156f0fa660SOndrej Zary {
2166f0fa660SOndrej Zary struct snd_card *card = pnp_get_card_drvdata(pcard);
2176f0fa660SOndrej Zary
2186f0fa660SOndrej Zary snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
2196f0fa660SOndrej Zary snd_ad1816a_suspend(card->private_data);
2206f0fa660SOndrej Zary return 0;
2216f0fa660SOndrej Zary }
2226f0fa660SOndrej Zary
snd_ad1816a_pnp_resume(struct pnp_card_link * pcard)2236f0fa660SOndrej Zary static int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard)
2246f0fa660SOndrej Zary {
2256f0fa660SOndrej Zary struct snd_card *card = pnp_get_card_drvdata(pcard);
2266f0fa660SOndrej Zary
2276f0fa660SOndrej Zary snd_ad1816a_resume(card->private_data);
2286f0fa660SOndrej Zary snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2296f0fa660SOndrej Zary return 0;
2306f0fa660SOndrej Zary }
2316f0fa660SOndrej Zary #endif
2326f0fa660SOndrej Zary
2331da177e4SLinus Torvalds static struct pnp_card_driver ad1816a_pnpc_driver = {
2341da177e4SLinus Torvalds .flags = PNP_DRIVER_RES_DISABLE,
2351da177e4SLinus Torvalds .name = "ad1816a",
2361da177e4SLinus Torvalds .id_table = snd_ad1816a_pnpids,
2371da177e4SLinus Torvalds .probe = snd_ad1816a_pnp_detect,
2386f0fa660SOndrej Zary #ifdef CONFIG_PM
2396f0fa660SOndrej Zary .suspend = snd_ad1816a_pnp_suspend,
2406f0fa660SOndrej Zary .resume = snd_ad1816a_pnp_resume,
2416f0fa660SOndrej Zary #endif
2421da177e4SLinus Torvalds };
2431da177e4SLinus Torvalds
alsa_card_ad1816a_init(void)2441da177e4SLinus Torvalds static int __init alsa_card_ad1816a_init(void)
2451da177e4SLinus Torvalds {
2465f53f4e2SBjorn Helgaas int err;
2471da177e4SLinus Torvalds
2485f53f4e2SBjorn Helgaas err = pnp_register_card_driver(&ad1816a_pnpc_driver);
2495f53f4e2SBjorn Helgaas if (err)
2505f53f4e2SBjorn Helgaas return err;
2515f53f4e2SBjorn Helgaas
2525f53f4e2SBjorn Helgaas if (!ad1816a_devices) {
2531da177e4SLinus Torvalds pnp_unregister_card_driver(&ad1816a_pnpc_driver);
254175cdcfbSTakashi Iwai #ifdef MODULE
2551da177e4SLinus Torvalds printk(KERN_ERR "no AD1816A based soundcards found.\n");
2561da177e4SLinus Torvalds #endif /* MODULE */
257175cdcfbSTakashi Iwai return -ENODEV;
258175cdcfbSTakashi Iwai }
259175cdcfbSTakashi Iwai return 0;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds
alsa_card_ad1816a_exit(void)2621da177e4SLinus Torvalds static void __exit alsa_card_ad1816a_exit(void)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds pnp_unregister_card_driver(&ad1816a_pnpc_driver);
2651da177e4SLinus Torvalds }
2661da177e4SLinus Torvalds
2671da177e4SLinus Torvalds module_init(alsa_card_ad1816a_init)
2681da177e4SLinus Torvalds module_exit(alsa_card_ad1816a_exit)
269