1 2 /* 3 card-als100.c - driver for Avance Logic ALS100 based soundcards. 4 Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> 5 6 Thanks to Pierfrancesco 'qM2' Passerini. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 #include <sound/driver.h> 24 #include <linux/init.h> 25 #include <linux/wait.h> 26 #include <linux/time.h> 27 #include <linux/pnp.h> 28 #include <linux/moduleparam.h> 29 #include <sound/core.h> 30 #include <sound/initval.h> 31 #include <sound/mpu401.h> 32 #include <sound/opl3.h> 33 #include <sound/sb.h> 34 35 #define PFX "als100: " 36 37 MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 38 MODULE_DESCRIPTION("Avance Logic ALS1X0"); 39 MODULE_LICENSE("GPL"); 40 MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS100 - PRO16PNP}," 41 "{Avance Logic,ALS110}," 42 "{Avance Logic,ALS120}," 43 "{Avance Logic,ALS200}," 44 "{3D Melody,MF1000}," 45 "{Digimate,3D Sound}," 46 "{Avance Logic,ALS120}," 47 "{RTL,RTL3000}}"); 48 49 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 50 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 51 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 52 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 53 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 54 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 55 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ 56 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ 57 static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 58 static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 59 60 module_param_array(index, int, NULL, 0444); 61 MODULE_PARM_DESC(index, "Index value for als100 based soundcard."); 62 module_param_array(id, charp, NULL, 0444); 63 MODULE_PARM_DESC(id, "ID string for als100 based soundcard."); 64 module_param_array(enable, bool, NULL, 0444); 65 MODULE_PARM_DESC(enable, "Enable als100 based soundcard."); 66 module_param_array(port, long, NULL, 0444); 67 MODULE_PARM_DESC(port, "Port # for als100 driver."); 68 module_param_array(mpu_port, long, NULL, 0444); 69 MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver."); 70 module_param_array(fm_port, long, NULL, 0444); 71 MODULE_PARM_DESC(fm_port, "FM port # for als100 driver."); 72 module_param_array(irq, int, NULL, 0444); 73 MODULE_PARM_DESC(irq, "IRQ # for als100 driver."); 74 module_param_array(mpu_irq, int, NULL, 0444); 75 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver."); 76 module_param_array(dma8, int, NULL, 0444); 77 MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver."); 78 module_param_array(dma16, int, NULL, 0444); 79 MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver."); 80 81 struct snd_card_als100 { 82 int dev_no; 83 struct pnp_dev *dev; 84 struct pnp_dev *devmpu; 85 struct pnp_dev *devopl; 86 }; 87 88 static struct pnp_card_device_id snd_als100_pnpids[] = { 89 /* ALS100 - PRO16PNP */ 90 { .id = "ALS0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } }, 91 /* ALS110 - MF1000 - Digimate 3D Sound */ 92 { .id = "ALS0110", .devs = { { "@@@1001" }, { "@X@1001" }, { "@H@1001" } } }, 93 /* ALS120 */ 94 { .id = "ALS0120", .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } } }, 95 /* ALS200 */ 96 { .id = "ALS0200", .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0001" } } }, 97 /* ALS200 OEM */ 98 { .id = "ALS0200", .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0020" } } }, 99 /* RTL3000 */ 100 { .id = "RTL3000", .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } } }, 101 { .id = "", } /* end */ 102 }; 103 104 MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids); 105 106 #define DRIVER_NAME "snd-card-als100" 107 108 static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, 109 struct pnp_card_link *card, 110 const struct pnp_card_device_id *id) 111 { 112 struct pnp_dev *pdev; 113 struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); 114 int err; 115 116 if (!cfg) 117 return -ENOMEM; 118 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); 119 if (acard->dev == NULL) { 120 kfree(cfg); 121 return -ENODEV; 122 } 123 acard->devmpu = pnp_request_card_device(card, id->devs[1].id, acard->dev); 124 acard->devopl = pnp_request_card_device(card, id->devs[2].id, acard->dev); 125 126 pdev = acard->dev; 127 128 pnp_init_resource_table(cfg); 129 130 /* override resources */ 131 if (port[dev] != SNDRV_AUTO_PORT) 132 pnp_resource_change(&cfg->port_resource[0], port[dev], 16); 133 if (dma8[dev] != SNDRV_AUTO_DMA) 134 pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); 135 if (dma16[dev] != SNDRV_AUTO_DMA) 136 pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1); 137 if (irq[dev] != SNDRV_AUTO_IRQ) 138 pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); 139 if (pnp_manual_config_dev(pdev, cfg, 0) < 0) 140 snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); 141 err = pnp_activate_dev(pdev); 142 if (err < 0) { 143 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); 144 kfree(cfg); 145 return err; 146 } 147 port[dev] = pnp_port_start(pdev, 0); 148 dma8[dev] = pnp_dma(pdev, 1); 149 dma16[dev] = pnp_dma(pdev, 0); 150 irq[dev] = pnp_irq(pdev, 0); 151 152 pdev = acard->devmpu; 153 if (pdev != NULL) { 154 pnp_init_resource_table(cfg); 155 if (mpu_port[dev] != SNDRV_AUTO_PORT) 156 pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); 157 if (mpu_irq[dev] != SNDRV_AUTO_IRQ) 158 pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); 159 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 160 snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n"); 161 err = pnp_activate_dev(pdev); 162 if (err < 0) 163 goto __mpu_error; 164 mpu_port[dev] = pnp_port_start(pdev, 0); 165 mpu_irq[dev] = pnp_irq(pdev, 0); 166 } else { 167 __mpu_error: 168 if (pdev) { 169 pnp_release_card_device(pdev); 170 snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n"); 171 } 172 acard->devmpu = NULL; 173 mpu_port[dev] = -1; 174 } 175 176 pdev = acard->devopl; 177 if (pdev != NULL) { 178 pnp_init_resource_table(cfg); 179 if (fm_port[dev] != SNDRV_AUTO_PORT) 180 pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4); 181 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 182 snd_printk(KERN_ERR PFX "OPL3 the requested resources are invalid, using auto config\n"); 183 err = pnp_activate_dev(pdev); 184 if (err < 0) 185 goto __fm_error; 186 fm_port[dev] = pnp_port_start(pdev, 0); 187 } else { 188 __fm_error: 189 if (pdev) { 190 pnp_release_card_device(pdev); 191 snd_printk(KERN_ERR PFX "OPL3 pnp configure failure, skipping\n"); 192 } 193 acard->devopl = NULL; 194 fm_port[dev] = -1; 195 } 196 197 kfree(cfg); 198 return 0; 199 } 200 201 static int __init snd_card_als100_probe(int dev, 202 struct pnp_card_link *pcard, 203 const struct pnp_card_device_id *pid) 204 { 205 int error; 206 sb_t *chip; 207 snd_card_t *card; 208 struct snd_card_als100 *acard; 209 opl3_t *opl3; 210 211 if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, 212 sizeof(struct snd_card_als100))) == NULL) 213 return -ENOMEM; 214 acard = (struct snd_card_als100 *)card->private_data; 215 216 if ((error = snd_card_als100_pnp(dev, acard, pcard, pid))) { 217 snd_card_free(card); 218 return error; 219 } 220 snd_card_set_dev(card, &pcard->card->dev); 221 222 if ((error = snd_sbdsp_create(card, port[dev], 223 irq[dev], 224 snd_sb16dsp_interrupt, 225 dma8[dev], 226 dma16[dev], 227 SB_HW_ALS100, &chip)) < 0) { 228 snd_card_free(card); 229 return error; 230 } 231 232 strcpy(card->driver, "ALS100"); 233 strcpy(card->shortname, "Avance Logic ALS100"); 234 sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", 235 card->shortname, chip->name, chip->port, 236 irq[dev], dma8[dev], dma16[dev]); 237 238 if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { 239 snd_card_free(card); 240 return error; 241 } 242 243 if ((error = snd_sbmixer_new(chip)) < 0) { 244 snd_card_free(card); 245 return error; 246 } 247 248 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { 249 if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100, 250 mpu_port[dev], 0, 251 mpu_irq[dev], SA_INTERRUPT, 252 NULL) < 0) 253 snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); 254 } 255 256 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { 257 if (snd_opl3_create(card, 258 fm_port[dev], fm_port[dev] + 2, 259 OPL3_HW_AUTO, 0, &opl3) < 0) { 260 snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", 261 fm_port[dev], fm_port[dev] + 2); 262 } else { 263 if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { 264 snd_card_free(card); 265 return error; 266 } 267 if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { 268 snd_card_free(card); 269 return error; 270 } 271 } 272 } 273 274 if ((error = snd_card_register(card)) < 0) { 275 snd_card_free(card); 276 return error; 277 } 278 pnp_set_card_drvdata(pcard, card); 279 return 0; 280 } 281 282 static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card, 283 const struct pnp_card_device_id *id) 284 { 285 static int dev; 286 int res; 287 288 for ( ; dev < SNDRV_CARDS; dev++) { 289 if (!enable[dev]) 290 continue; 291 res = snd_card_als100_probe(dev, card, id); 292 if (res < 0) 293 return res; 294 dev++; 295 return 0; 296 } 297 return -ENODEV; 298 } 299 300 static void __devexit snd_als100_pnp_remove(struct pnp_card_link * pcard) 301 { 302 snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); 303 304 snd_card_disconnect(card); 305 snd_card_free_in_thread(card); 306 } 307 308 static struct pnp_card_driver als100_pnpc_driver = { 309 .flags = PNP_DRIVER_RES_DISABLE, 310 .name = "als100", 311 .id_table = snd_als100_pnpids, 312 .probe = snd_als100_pnp_detect, 313 .remove = __devexit_p(snd_als100_pnp_remove), 314 }; 315 316 static int __init alsa_card_als100_init(void) 317 { 318 int cards = 0; 319 320 cards += pnp_register_card_driver(&als100_pnpc_driver); 321 #ifdef MODULE 322 if (!cards) { 323 pnp_unregister_card_driver(&als100_pnpc_driver); 324 snd_printk(KERN_ERR "no ALS100 based soundcards found\n"); 325 } 326 #endif 327 return cards ? 0 : -ENODEV; 328 } 329 330 static void __exit alsa_card_als100_exit(void) 331 { 332 pnp_unregister_card_driver(&als100_pnpc_driver); 333 } 334 335 module_init(alsa_card_als100_init) 336 module_exit(alsa_card_als100_exit) 337