1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 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't 11 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 WSS 17 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 33 MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 34 MODULE_DESCRIPTION("Aztech Systems AZT2320"); 35 MODULE_LICENSE("GPL"); 36 37 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 38 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 39 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 40 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 41 static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 42 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 43 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 44 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ 45 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ 46 static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 47 static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 48 49 module_param_array(index, int, NULL, 0444); 50 MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard."); 51 module_param_array(id, charp, NULL, 0444); 52 MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard."); 53 module_param_array(enable, bool, NULL, 0444); 54 MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard."); 55 56 struct snd_card_azt2320 { 57 int dev_no; 58 struct pnp_dev *dev; 59 struct pnp_dev *devmpu; 60 struct snd_wss *chip; 61 }; 62 63 static 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 79 MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids); 80 81 #define DRIVER_NAME "snd-card-azt2320" 82 83 static 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 */ 131 static 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 145 static 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 160 static 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 237 static unsigned int azt2320_devices; 238 239 static 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_PM 259 static 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 270 static 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 #endif 281 282 static 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_PM 288 .suspend = snd_azt2320_pnp_suspend, 289 .resume = snd_azt2320_pnp_resume, 290 #endif 291 }; 292 293 static 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 MODULE 304 pr_err("no AZT2320 based soundcards found\n"); 305 #endif 306 return -ENODEV; 307 } 308 return 0; 309 } 310 311 static void __exit alsa_card_azt2320_exit(void) 312 { 313 pnp_unregister_card_driver(&azt2320_pnpc_driver); 314 } 315 316 module_init(alsa_card_azt2320_init) 317 module_exit(alsa_card_azt2320_exit) 318