1 2/* 3 card-es968.c - driver for ESS AudioDrive ES968 based soundcards. 4 Copyright (C) 1999 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/time.h> 26#include <linux/pnp.h> 27#include <linux/moduleparam.h> 28#include <sound/core.h> 29#include <sound/initval.h> 30#include <sound/sb.h> 31 32#define PFX "es968: " 33 34MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 35MODULE_DESCRIPTION("ESS AudioDrive ES968"); 36MODULE_LICENSE("GPL"); 37MODULE_SUPPORTED_DEVICE("{{ESS,AudioDrive ES968}}"); 38 39static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 40static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 41static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 42static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ 43static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ 44static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ 45 46module_param_array(index, int, NULL, 0444); 47MODULE_PARM_DESC(index, "Index value for es968 based soundcard."); 48module_param_array(id, charp, NULL, 0444); 49MODULE_PARM_DESC(id, "ID string for es968 based soundcard."); 50module_param_array(enable, bool, NULL, 0444); 51MODULE_PARM_DESC(enable, "Enable es968 based soundcard."); 52module_param_array(port, long, NULL, 0444); 53MODULE_PARM_DESC(port, "Port # for es968 driver."); 54module_param_array(irq, int, NULL, 0444); 55MODULE_PARM_DESC(irq, "IRQ # for es968 driver."); 56module_param_array(dma8, int, NULL, 0444); 57MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver."); 58 59struct snd_card_es968 { 60 struct pnp_dev *dev; 61 struct snd_sb *chip; 62}; 63 64static struct pnp_card_device_id snd_es968_pnpids[] = { 65 { .id = "ESS0968", .devs = { { "@@@0968" }, } }, 66 { .id = "", } /* end */ 67}; 68 69MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids); 70 71#define DRIVER_NAME "snd-card-es968" 72 73static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id) 74{ 75 struct snd_sb *chip = dev_id; 76 77 if (chip->open & SB_OPEN_PCM) { 78 return snd_sb8dsp_interrupt(chip); 79 } else { 80 return snd_sb8dsp_midi_interrupt(chip); 81 } 82} 83 84static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard, 85 struct pnp_card_link *card, 86 const struct pnp_card_device_id *id) 87{ 88 struct pnp_dev *pdev; 89 struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); 90 int err; 91 if (!cfg) 92 return -ENOMEM; 93 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); 94 if (acard->dev == NULL) { 95 kfree(cfg); 96 return -ENODEV; 97 } 98 99 pdev = acard->dev; 100 101 pnp_init_resource_table(cfg); 102 103 /* override resources */ 104 if (port[dev] != SNDRV_AUTO_PORT) 105 pnp_resource_change(&cfg->port_resource[0], port[dev], 16); 106 if (dma8[dev] != SNDRV_AUTO_DMA) 107 pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); 108 if (irq[dev] != SNDRV_AUTO_IRQ) 109 pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); 110 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 111 snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); 112 err = pnp_activate_dev(pdev); 113 if (err < 0) { 114 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); 115 kfree(cfg); 116 return err; 117 } 118 port[dev] = pnp_port_start(pdev, 0); 119 dma8[dev] = pnp_dma(pdev, 1); 120 irq[dev] = pnp_irq(pdev, 0); 121 122 kfree(cfg); 123 return 0; 124} 125 126static int __devinit snd_card_es968_probe(int dev, 127 struct pnp_card_link *pcard, 128 const struct pnp_card_device_id *pid) 129{ 130 int error; 131 struct snd_sb *chip; 132 struct snd_card *card; 133 struct snd_card_es968 *acard; 134 135 if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, 136 sizeof(struct snd_card_es968))) == NULL) 137 return -ENOMEM; 138 acard = card->private_data; 139 if ((error = snd_card_es968_pnp(dev, acard, pcard, pid))) { 140 snd_card_free(card); 141 return error; 142 } 143 snd_card_set_dev(card, &pcard->card->dev); 144 145 if ((error = snd_sbdsp_create(card, port[dev], 146 irq[dev], 147 snd_card_es968_interrupt, 148 dma8[dev], 149 -1, 150 SB_HW_AUTO, &chip)) < 0) { 151 snd_card_free(card); 152 return error; 153 } 154 acard->chip = chip; 155 156 if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { 157 snd_card_free(card); 158 return error; 159 } 160 161 if ((error = snd_sbmixer_new(chip)) < 0) { 162 snd_card_free(card); 163 return error; 164 } 165 166 if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { 167 snd_card_free(card); 168 return error; 169 } 170 171 strcpy(card->driver, "ES968"); 172 strcpy(card->shortname, "ESS ES968"); 173 sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", 174 card->shortname, chip->name, chip->port, irq[dev], dma8[dev]); 175 176 if ((error = snd_card_register(card)) < 0) { 177 snd_card_free(card); 178 return error; 179 } 180 pnp_set_card_drvdata(pcard, card); 181 return 0; 182} 183 184static unsigned int __devinitdata es968_devices; 185 186static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card, 187 const struct pnp_card_device_id *id) 188{ 189 static int dev; 190 int res; 191 192 for ( ; dev < SNDRV_CARDS; dev++) { 193 if (!enable[dev]) 194 continue; 195 res = snd_card_es968_probe(dev, card, id); 196 if (res < 0) 197 return res; 198 dev++; 199 es968_devices++; 200 return 0; 201 } 202 return -ENODEV; 203} 204 205static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard) 206{ 207 snd_card_free(pnp_get_card_drvdata(pcard)); 208 pnp_set_card_drvdata(pcard, NULL); 209} 210 211#ifdef CONFIG_PM 212static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) 213{ 214 struct snd_card *card = pnp_get_card_drvdata(pcard); 215 struct snd_card_es968 *acard = card->private_data; 216 struct snd_sb *chip = acard->chip; 217 218 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 219 snd_pcm_suspend_all(chip->pcm); 220 snd_sbmixer_suspend(chip); 221 return 0; 222} 223 224static int snd_es968_pnp_resume(struct pnp_card_link *pcard) 225{ 226 struct snd_card *card = pnp_get_card_drvdata(pcard); 227 struct snd_card_es968 *acard = card->private_data; 228 struct snd_sb *chip = acard->chip; 229 230 snd_sbdsp_reset(chip); 231 snd_sbmixer_resume(chip); 232 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 233 return 0; 234} 235#endif 236 237static struct pnp_card_driver es968_pnpc_driver = { 238 .flags = PNP_DRIVER_RES_DISABLE, 239 .name = "es968", 240 .id_table = snd_es968_pnpids, 241 .probe = snd_es968_pnp_detect, 242 .remove = __devexit_p(snd_es968_pnp_remove), 243#ifdef CONFIG_PM 244 .suspend = snd_es968_pnp_suspend, 245 .resume = snd_es968_pnp_resume, 246#endif 247}; 248 249static int __init alsa_card_es968_init(void) 250{ 251 int err = pnp_register_card_driver(&es968_pnpc_driver); 252 if (err) 253 return err; 254 255 if (!es968_devices) { 256 pnp_unregister_card_driver(&es968_pnpc_driver); 257#ifdef MODULE 258 snd_printk(KERN_ERR "no ES968 based soundcards found\n"); 259#endif 260 return -ENODEV; 261 } 262 return 0; 263} 264 265static void __exit alsa_card_es968_exit(void) 266{ 267 pnp_unregister_card_driver(&es968_pnpc_driver); 268} 269 270module_init(alsa_card_es968_init) 271module_exit(alsa_card_es968_exit) 272