1 /* 2 card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards. 3 Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20 /* 21 This driver should provide support for most Aztech AZT2320 based cards. 22 Several AZT2316 chips are also supported/tested, but autoprobe doesn't 23 work: all module option have to be set. 24 25 No docs available for us at Aztech headquarters !!! Unbelievable ... 26 No other help obtained. 27 28 Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS 29 activation method (full-duplex audio!). 30 */ 31 32 #include <sound/driver.h> 33 #include <asm/io.h> 34 #include <linux/delay.h> 35 #include <linux/init.h> 36 #include <linux/time.h> 37 #include <linux/wait.h> 38 #include <linux/pnp.h> 39 #include <linux/moduleparam.h> 40 #include <sound/core.h> 41 #include <sound/initval.h> 42 #include <sound/cs4231.h> 43 #include <sound/mpu401.h> 44 #include <sound/opl3.h> 45 46 #define PFX "azt2320: " 47 48 MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 49 MODULE_DESCRIPTION("Aztech Systems AZT2320"); 50 MODULE_LICENSE("GPL"); 51 MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V}," 52 "{Aztech Systems,AZT2320}," 53 "{Aztech Systems,AZT3300}," 54 "{Aztech Systems,AZT2320}," 55 "{Aztech Systems,AZT3000}}"); 56 57 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 58 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 59 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 60 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 61 static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 62 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 63 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 64 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ 65 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ 66 static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 67 static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 68 69 module_param_array(index, int, NULL, 0444); 70 MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard."); 71 module_param_array(id, charp, NULL, 0444); 72 MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard."); 73 module_param_array(enable, bool, NULL, 0444); 74 MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard."); 75 module_param_array(port, long, NULL, 0444); 76 MODULE_PARM_DESC(port, "Port # for azt2320 driver."); 77 module_param_array(wss_port, long, NULL, 0444); 78 MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver."); 79 module_param_array(mpu_port, long, NULL, 0444); 80 MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver."); 81 module_param_array(fm_port, long, NULL, 0444); 82 MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver."); 83 module_param_array(irq, int, NULL, 0444); 84 MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver."); 85 module_param_array(mpu_irq, int, NULL, 0444); 86 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver."); 87 module_param_array(dma1, int, NULL, 0444); 88 MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver."); 89 module_param_array(dma2, int, NULL, 0444); 90 MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver."); 91 92 struct snd_card_azt2320 { 93 int dev_no; 94 struct pnp_dev *dev; 95 struct pnp_dev *devmpu; 96 struct snd_cs4231 *chip; 97 }; 98 99 static struct pnp_card_device_id snd_azt2320_pnpids[] = { 100 /* PRO16V */ 101 { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } }, 102 /* Aztech Sound Galaxy 16 */ 103 { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } }, 104 /* Packard Bell Sound III 336 AM/SP */ 105 { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } }, 106 /* AT3300 */ 107 { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } }, 108 /* --- */ 109 { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } }, 110 /* --- */ 111 { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } }, 112 { .id = "" } /* end */ 113 }; 114 115 MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids); 116 117 #define DRIVER_NAME "snd-card-azt2320" 118 119 static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard, 120 struct pnp_card_link *card, 121 const struct pnp_card_device_id *id) 122 { 123 struct pnp_dev *pdev; 124 struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); 125 int err; 126 127 if (!cfg) 128 return -ENOMEM; 129 130 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); 131 if (acard->dev == NULL) { 132 kfree(cfg); 133 return -ENODEV; 134 } 135 136 acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL); 137 138 pdev = acard->dev; 139 pnp_init_resource_table(cfg); 140 141 /* override resources */ 142 if (port[dev] != SNDRV_AUTO_PORT) 143 pnp_resource_change(&cfg->port_resource[0], port[dev], 16); 144 if (fm_port[dev] != SNDRV_AUTO_PORT) 145 pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); 146 if (wss_port[dev] != SNDRV_AUTO_PORT) 147 pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4); 148 if (dma1[dev] != SNDRV_AUTO_DMA) 149 pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); 150 if (dma2[dev] != SNDRV_AUTO_DMA) 151 pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); 152 if (irq[dev] != SNDRV_AUTO_IRQ) 153 pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); 154 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 155 snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); 156 157 err = pnp_activate_dev(pdev); 158 if (err < 0) { 159 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); 160 kfree(cfg); 161 return err; 162 } 163 port[dev] = pnp_port_start(pdev, 0); 164 fm_port[dev] = pnp_port_start(pdev, 1); 165 wss_port[dev] = pnp_port_start(pdev, 2); 166 dma1[dev] = pnp_dma(pdev, 0); 167 dma2[dev] = pnp_dma(pdev, 1); 168 irq[dev] = pnp_irq(pdev, 0); 169 170 pdev = acard->devmpu; 171 if (pdev != NULL) { 172 pnp_init_resource_table(cfg); 173 if (mpu_port[dev] != SNDRV_AUTO_PORT) 174 pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); 175 if (mpu_irq[dev] != SNDRV_AUTO_IRQ) 176 pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); 177 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 178 snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n"); 179 err = pnp_activate_dev(pdev); 180 if (err < 0) 181 goto __mpu_error; 182 mpu_port[dev] = pnp_port_start(pdev, 0); 183 mpu_irq[dev] = pnp_irq(pdev, 0); 184 } else { 185 __mpu_error: 186 if (pdev) { 187 pnp_release_card_device(pdev); 188 snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n"); 189 } 190 acard->devmpu = NULL; 191 mpu_port[dev] = -1; 192 } 193 194 kfree (cfg); 195 return 0; 196 } 197 198 /* same of snd_sbdsp_command by Jaroslav Kysela */ 199 static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val) 200 { 201 int i; 202 unsigned long limit; 203 204 limit = jiffies + HZ / 10; 205 for (i = 50000; i && time_after(limit, jiffies); i--) 206 if (!(inb(port + 0x0c) & 0x80)) { 207 outb(val, port + 0x0c); 208 return 0; 209 } 210 return -EBUSY; 211 } 212 213 static int __devinit snd_card_azt2320_enable_wss(unsigned long port) 214 { 215 int error; 216 217 if ((error = snd_card_azt2320_command(port, 0x09))) 218 return error; 219 if ((error = snd_card_azt2320_command(port, 0x00))) 220 return error; 221 222 mdelay(5); 223 return 0; 224 } 225 226 static int __devinit snd_card_azt2320_probe(int dev, 227 struct pnp_card_link *pcard, 228 const struct pnp_card_device_id *pid) 229 { 230 int error; 231 struct snd_card *card; 232 struct snd_card_azt2320 *acard; 233 struct snd_cs4231 *chip; 234 struct snd_opl3 *opl3; 235 236 if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, 237 sizeof(struct snd_card_azt2320))) == NULL) 238 return -ENOMEM; 239 acard = (struct snd_card_azt2320 *)card->private_data; 240 241 if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) { 242 snd_card_free(card); 243 return error; 244 } 245 snd_card_set_dev(card, &pcard->card->dev); 246 247 if ((error = snd_card_azt2320_enable_wss(port[dev]))) { 248 snd_card_free(card); 249 return error; 250 } 251 252 if ((error = snd_cs4231_create(card, wss_port[dev], -1, 253 irq[dev], 254 dma1[dev], 255 dma2[dev], 256 CS4231_HW_DETECT, 0, &chip)) < 0) { 257 snd_card_free(card); 258 return error; 259 } 260 261 strcpy(card->driver, "AZT2320"); 262 strcpy(card->shortname, "Aztech AZT2320"); 263 sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i", 264 card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); 265 266 if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) { 267 snd_card_free(card); 268 return error; 269 } 270 if ((error = snd_cs4231_mixer(chip)) < 0) { 271 snd_card_free(card); 272 return error; 273 } 274 if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) { 275 snd_card_free(card); 276 return error; 277 } 278 279 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { 280 if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320, 281 mpu_port[dev], 0, 282 mpu_irq[dev], SA_INTERRUPT, 283 NULL) < 0) 284 snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); 285 } 286 287 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { 288 if (snd_opl3_create(card, 289 fm_port[dev], fm_port[dev] + 2, 290 OPL3_HW_AUTO, 0, &opl3) < 0) { 291 snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", 292 fm_port[dev], fm_port[dev] + 2); 293 } else { 294 if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { 295 snd_card_free(card); 296 return error; 297 } 298 if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { 299 snd_card_free(card); 300 return error; 301 } 302 } 303 } 304 305 if ((error = snd_card_register(card)) < 0) { 306 snd_card_free(card); 307 return error; 308 } 309 pnp_set_card_drvdata(pcard, card); 310 return 0; 311 } 312 313 static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card, 314 const struct pnp_card_device_id *id) 315 { 316 static int dev; 317 int res; 318 319 for ( ; dev < SNDRV_CARDS; dev++) { 320 if (!enable[dev]) 321 continue; 322 res = snd_card_azt2320_probe(dev, card, id); 323 if (res < 0) 324 return res; 325 dev++; 326 return 0; 327 } 328 return -ENODEV; 329 } 330 331 static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard) 332 { 333 snd_card_free(pnp_get_card_drvdata(pcard)); 334 pnp_set_card_drvdata(pcard, NULL); 335 } 336 337 #ifdef CONFIG_PM 338 static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) 339 { 340 struct snd_card *card = pnp_get_card_drvdata(pcard); 341 struct snd_card_azt2320 *acard = card->private_data; 342 struct snd_cs4231 *chip = acard->chip; 343 344 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 345 chip->suspend(chip); 346 return 0; 347 } 348 349 static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard) 350 { 351 struct snd_card *card = pnp_get_card_drvdata(pcard); 352 struct snd_card_azt2320 *acard = card->private_data; 353 struct snd_cs4231 *chip = acard->chip; 354 355 chip->resume(chip); 356 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 357 return 0; 358 } 359 #endif 360 361 static struct pnp_card_driver azt2320_pnpc_driver = { 362 .flags = PNP_DRIVER_RES_DISABLE, 363 .name = "azt2320", 364 .id_table = snd_azt2320_pnpids, 365 .probe = snd_azt2320_pnp_detect, 366 .remove = __devexit_p(snd_azt2320_pnp_remove), 367 #ifdef CONFIG_PM 368 .suspend = snd_azt2320_pnp_suspend, 369 .resume = snd_azt2320_pnp_resume, 370 #endif 371 }; 372 373 static int __init alsa_card_azt2320_init(void) 374 { 375 int cards; 376 377 cards = pnp_register_card_driver(&azt2320_pnpc_driver); 378 if (cards <= 0) { 379 pnp_unregister_card_driver(&azt2320_pnpc_driver); 380 #ifdef MODULE 381 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n"); 382 #endif 383 return -ENODEV; 384 } 385 return 0; 386 } 387 388 static void __exit alsa_card_azt2320_exit(void) 389 { 390 pnp_unregister_card_driver(&azt2320_pnpc_driver); 391 } 392 393 module_init(alsa_card_azt2320_init) 394 module_exit(alsa_card_azt2320_exit) 395