318 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.4 Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>5 6*/7 8/*9 This driver should provide support for most Aztech AZT2320 based cards.10 Several AZT2316 chips are also supported/tested, but autoprobe doesn't11 work: all module option have to be set.12 13 No docs available for us at Aztech headquarters !!! Unbelievable ...14 No other help obtained.15 16 Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS17 activation method (full-duplex audio!).18*/19 20#include <linux/io.h>21#include <linux/delay.h>22#include <linux/init.h>23#include <linux/time.h>24#include <linux/wait.h>25#include <linux/pnp.h>26#include <linux/module.h>27#include <sound/core.h>28#include <sound/initval.h>29#include <sound/wss.h>30#include <sound/mpu401.h>31#include <sound/opl3.h>32 33MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");34MODULE_DESCRIPTION("Aztech Systems AZT2320");35MODULE_LICENSE("GPL");36 37static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */38static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */39static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */40static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */41static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */42static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */43static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */44static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */45static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */46static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */47static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */48 49module_param_array(index, int, NULL, 0444);50MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");51module_param_array(id, charp, NULL, 0444);52MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");53module_param_array(enable, bool, NULL, 0444);54MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");55 56struct snd_card_azt2320 {57 int dev_no;58 struct pnp_dev *dev;59 struct pnp_dev *devmpu;60 struct snd_wss *chip;61};62 63static const struct pnp_card_device_id snd_azt2320_pnpids[] = {64 /* PRO16V */65 { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },66 /* Aztech Sound Galaxy 16 */67 { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },68 /* Packard Bell Sound III 336 AM/SP */69 { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },70 /* AT3300 */71 { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },72 /* --- */73 { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },74 /* --- */75 { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },76 { .id = "" } /* end */77};78 79MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);80 81#define DRIVER_NAME "snd-card-azt2320"82 83static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,84 struct pnp_card_link *card,85 const struct pnp_card_device_id *id)86{87 struct pnp_dev *pdev;88 int err;89 90 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);91 if (acard->dev == NULL)92 return -ENODEV;93 94 acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);95 96 pdev = acard->dev;97 98 err = pnp_activate_dev(pdev);99 if (err < 0) {100 dev_err(&pdev->dev, "AUDIO pnp configure failure\n");101 return err;102 }103 port[dev] = pnp_port_start(pdev, 0);104 fm_port[dev] = pnp_port_start(pdev, 1);105 wss_port[dev] = pnp_port_start(pdev, 2);106 dma1[dev] = pnp_dma(pdev, 0);107 dma2[dev] = pnp_dma(pdev, 1);108 irq[dev] = pnp_irq(pdev, 0);109 110 pdev = acard->devmpu;111 if (pdev != NULL) {112 err = pnp_activate_dev(pdev);113 if (err < 0)114 goto __mpu_error;115 mpu_port[dev] = pnp_port_start(pdev, 0);116 mpu_irq[dev] = pnp_irq(pdev, 0);117 } else {118 __mpu_error:119 if (pdev) {120 pnp_release_card_device(pdev);121 dev_err(&pdev->dev, "MPU401 pnp configure failure, skipping\n");122 }123 acard->devmpu = NULL;124 mpu_port[dev] = -1;125 }126 127 return 0;128}129 130/* same of snd_sbdsp_command by Jaroslav Kysela */131static int snd_card_azt2320_command(unsigned long port, unsigned char val)132{133 int i;134 unsigned long limit;135 136 limit = jiffies + HZ / 10;137 for (i = 50000; i && time_after(limit, jiffies); i--)138 if (!(inb(port + 0x0c) & 0x80)) {139 outb(val, port + 0x0c);140 return 0;141 }142 return -EBUSY;143}144 145static int snd_card_azt2320_enable_wss(unsigned long port)146{147 int error;148 149 error = snd_card_azt2320_command(port, 0x09);150 if (error)151 return error;152 error = snd_card_azt2320_command(port, 0x00);153 if (error)154 return error;155 156 mdelay(5);157 return 0;158}159 160static int snd_card_azt2320_probe(int dev,161 struct pnp_card_link *pcard,162 const struct pnp_card_device_id *pid)163{164 int error;165 struct snd_card *card;166 struct snd_card_azt2320 *acard;167 struct snd_wss *chip;168 struct snd_opl3 *opl3;169 170 error = snd_devm_card_new(&pcard->card->dev,171 index[dev], id[dev], THIS_MODULE,172 sizeof(struct snd_card_azt2320), &card);173 if (error < 0)174 return error;175 acard = card->private_data;176 177 error = snd_card_azt2320_pnp(dev, acard, pcard, pid);178 if (error)179 return error;180 181 error = snd_card_azt2320_enable_wss(port[dev]);182 if (error)183 return error;184 185 error = snd_wss_create(card, wss_port[dev], -1,186 irq[dev],187 dma1[dev], dma2[dev],188 WSS_HW_DETECT, 0, &chip);189 if (error < 0)190 return error;191 192 strcpy(card->driver, "AZT2320");193 strcpy(card->shortname, "Aztech AZT2320");194 sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",195 card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);196 197 error = snd_wss_pcm(chip, 0);198 if (error < 0)199 return error;200 error = snd_wss_mixer(chip);201 if (error < 0)202 return error;203 error = snd_wss_timer(chip, 0);204 if (error < 0)205 return error;206 207 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {208 if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,209 mpu_port[dev], 0,210 mpu_irq[dev], NULL) < 0)211 dev_err(card->dev, "no MPU-401 device at 0x%lx\n", mpu_port[dev]);212 }213 214 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {215 if (snd_opl3_create(card,216 fm_port[dev], fm_port[dev] + 2,217 OPL3_HW_AUTO, 0, &opl3) < 0) {218 dev_err(card->dev, "no OPL device at 0x%lx-0x%lx\n",219 fm_port[dev], fm_port[dev] + 2);220 } else {221 error = snd_opl3_timer_new(opl3, 1, 2);222 if (error < 0)223 return error;224 error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);225 if (error < 0)226 return error;227 }228 }229 230 error = snd_card_register(card);231 if (error < 0)232 return error;233 pnp_set_card_drvdata(pcard, card);234 return 0;235}236 237static unsigned int azt2320_devices;238 239static int snd_azt2320_pnp_detect(struct pnp_card_link *card,240 const struct pnp_card_device_id *id)241{242 static int dev;243 int res;244 245 for ( ; dev < SNDRV_CARDS; dev++) {246 if (!enable[dev])247 continue;248 res = snd_card_azt2320_probe(dev, card, id);249 if (res < 0)250 return res;251 dev++;252 azt2320_devices++;253 return 0;254 }255 return -ENODEV;256}257 258#ifdef CONFIG_PM259static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)260{261 struct snd_card *card = pnp_get_card_drvdata(pcard);262 struct snd_card_azt2320 *acard = card->private_data;263 struct snd_wss *chip = acard->chip;264 265 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);266 chip->suspend(chip);267 return 0;268}269 270static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)271{272 struct snd_card *card = pnp_get_card_drvdata(pcard);273 struct snd_card_azt2320 *acard = card->private_data;274 struct snd_wss *chip = acard->chip;275 276 chip->resume(chip);277 snd_power_change_state(card, SNDRV_CTL_POWER_D0);278 return 0;279}280#endif281 282static struct pnp_card_driver azt2320_pnpc_driver = {283 .flags = PNP_DRIVER_RES_DISABLE,284 .name = "azt2320",285 .id_table = snd_azt2320_pnpids,286 .probe = snd_azt2320_pnp_detect,287#ifdef CONFIG_PM288 .suspend = snd_azt2320_pnp_suspend,289 .resume = snd_azt2320_pnp_resume,290#endif291};292 293static int __init alsa_card_azt2320_init(void)294{295 int err;296 297 err = pnp_register_card_driver(&azt2320_pnpc_driver);298 if (err)299 return err;300 301 if (!azt2320_devices) {302 pnp_unregister_card_driver(&azt2320_pnpc_driver);303#ifdef MODULE304 pr_err("no AZT2320 based soundcards found\n");305#endif306 return -ENODEV;307 }308 return 0;309}310 311static void __exit alsa_card_azt2320_exit(void)312{313 pnp_unregister_card_driver(&azt2320_pnpc_driver);314}315 316module_init(alsa_card_azt2320_init)317module_exit(alsa_card_azt2320_exit)318