1/* 2 * Driver for Aztech Sound Galaxy cards 3 * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk. 4 * 5 * I don't have documentation for this card, I based this driver on the 6 * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c) 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 24#include <linux/init.h> 25#include <linux/err.h> 26#include <linux/isa.h> 27#include <linux/delay.h> 28#include <linux/time.h> 29#include <linux/interrupt.h> 30#include <linux/moduleparam.h> 31#include <asm/dma.h> 32#include <sound/core.h> 33#include <sound/sb.h> 34#include <sound/wss.h> 35#include <sound/control.h> 36#define SNDRV_LEGACY_FIND_FREE_IRQ 37#define SNDRV_LEGACY_FIND_FREE_DMA 38#include <sound/initval.h> 39 40MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>"); 41MODULE_DESCRIPTION("Aztech Sound Galaxy"); 42MODULE_LICENSE("GPL"); 43MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}"); 44 45static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 46static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 47static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 48static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ 49static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ 50static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ 51static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ 52 53module_param_array(index, int, NULL, 0444); 54MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); 55module_param_array(id, charp, NULL, 0444); 56MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); 57module_param_array(sbport, long, NULL, 0444); 58MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); 59module_param_array(wssport, long, NULL, 0444); 60MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); 61module_param_array(irq, int, NULL, 0444); 62MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); 63module_param_array(dma1, int, NULL, 0444); 64MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); 65 66#define SGALAXY_AUXC_LEFT 18 67#define SGALAXY_AUXC_RIGHT 19 68 69#define PFX "sgalaxy: " 70 71/* 72 73 */ 74 75#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) 76 77/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */ 78/* short time we actually need it.. */ 79 80static int snd_sgalaxy_sbdsp_reset(unsigned long port) 81{ 82 int i; 83 84 outb(1, SBP1(port, RESET)); 85 udelay(10); 86 outb(0, SBP1(port, RESET)); 87 udelay(30); 88 for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); 89 if (inb(SBP1(port, READ)) != 0xaa) { 90 snd_printd("sb_reset: failed at 0x%lx!!!\n", port); 91 return -ENODEV; 92 } 93 return 0; 94} 95 96static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port, 97 unsigned char val) 98{ 99 int i; 100 101 for (i = 10000; i; i--) 102 if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { 103 outb(val, SBP1(port, COMMAND)); 104 return 1; 105 } 106 107 return 0; 108} 109 110static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id) 111{ 112 return IRQ_NONE; 113} 114 115static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) 116{ 117 static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 118 0x10, 0x18, 0x20, -1, -1, -1, -1}; 119 static int dma_bits[] = {1, 2, 0, 3}; 120 int tmp, tmp1; 121 122 if ((tmp = inb(port + 3)) == 0xff) 123 { 124 snd_printdd("I/O address dead (0x%lx)\n", port); 125 return 0; 126 } 127 128 if ((tmp & 0x3f) != 0x04 && 129 (tmp & 0x3f) != 0x0f && 130 (tmp & 0x3f) != 0x00) { 131 snd_printdd("No WSS signature detected on port 0x%lx\n", 132 port + 3); 133 return 0; 134 } 135 136 137 /* initialize IRQ for WSS codec */ 138 tmp = interrupt_bits[irq % 16]; 139 if (tmp < 0) 140 return -EINVAL; 141 142 if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) { 143 snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq); 144 return -EIO; 145 } 146 147 outb(tmp | 0x40, port); 148 tmp1 = dma_bits[dma % 4]; 149 outb(tmp | tmp1, port); 150 151 free_irq(irq, NULL); 152 153 return 0; 154} 155 156static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma) 157{ 158 159 /* switch to WSS mode */ 160 snd_sgalaxy_sbdsp_reset(sbport[dev]); 161 162 snd_sgalaxy_sbdsp_command(sbport[dev], 9); 163 snd_sgalaxy_sbdsp_command(sbport[dev], 0); 164 165 udelay(400); 166 return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); 167} 168 169static struct snd_kcontrol_new snd_sgalaxy_controls[] = { 170WSS_DOUBLE("Aux Playback Switch", 0, 171 SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), 172WSS_DOUBLE("Aux Playback Volume", 0, 173 SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) 174}; 175 176static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip) 177{ 178 struct snd_card *card = chip->card; 179 struct snd_ctl_elem_id id1, id2; 180 unsigned int idx; 181 int err; 182 183 memset(&id1, 0, sizeof(id1)); 184 memset(&id2, 0, sizeof(id2)); 185 id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 186 /* reassign AUX0 to LINE */ 187 strcpy(id1.name, "Aux Playback Switch"); 188 strcpy(id2.name, "Line Playback Switch"); 189 if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 190 return err; 191 strcpy(id1.name, "Aux Playback Volume"); 192 strcpy(id2.name, "Line Playback Volume"); 193 if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 194 return err; 195 /* reassign AUX1 to FM */ 196 strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 197 strcpy(id2.name, "FM Playback Switch"); 198 if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 199 return err; 200 strcpy(id1.name, "Aux Playback Volume"); 201 strcpy(id2.name, "FM Playback Volume"); 202 if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 203 return err; 204 /* build AUX2 input */ 205 for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { 206 err = snd_ctl_add(card, 207 snd_ctl_new1(&snd_sgalaxy_controls[idx], chip)); 208 if (err < 0) 209 return err; 210 } 211 return 0; 212} 213 214static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev) 215{ 216 if (!enable[dev]) 217 return 0; 218 if (sbport[dev] == SNDRV_AUTO_PORT) { 219 snd_printk(KERN_ERR PFX "specify SB port\n"); 220 return 0; 221 } 222 if (wssport[dev] == SNDRV_AUTO_PORT) { 223 snd_printk(KERN_ERR PFX "specify WSS port\n"); 224 return 0; 225 } 226 return 1; 227} 228 229static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev) 230{ 231 static int possible_irqs[] = {7, 9, 10, 11, -1}; 232 static int possible_dmas[] = {1, 3, 0, -1}; 233 int err, xirq, xdma1; 234 struct snd_card *card; 235 struct snd_wss *chip; 236 237 err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); 238 if (err < 0) 239 return err; 240 241 xirq = irq[dev]; 242 if (xirq == SNDRV_AUTO_IRQ) { 243 if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { 244 snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 245 err = -EBUSY; 246 goto _err; 247 } 248 } 249 xdma1 = dma1[dev]; 250 if (xdma1 == SNDRV_AUTO_DMA) { 251 if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { 252 snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); 253 err = -EBUSY; 254 goto _err; 255 } 256 } 257 258 if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0) 259 goto _err; 260 261 err = snd_wss_create(card, wssport[dev] + 4, -1, 262 xirq, xdma1, -1, 263 WSS_HW_DETECT, 0, &chip); 264 if (err < 0) 265 goto _err; 266 card->private_data = chip; 267 268 err = snd_wss_pcm(chip, 0, NULL); 269 if (err < 0) { 270 snd_printdd(PFX "error creating new WSS PCM device\n"); 271 goto _err; 272 } 273 err = snd_wss_mixer(chip); 274 if (err < 0) { 275 snd_printdd(PFX "error creating new WSS mixer\n"); 276 goto _err; 277 } 278 if ((err = snd_sgalaxy_mixer(chip)) < 0) { 279 snd_printdd(PFX "the mixer rewrite failed\n"); 280 goto _err; 281 } 282 283 strcpy(card->driver, "Sound Galaxy"); 284 strcpy(card->shortname, "Sound Galaxy"); 285 sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", 286 wssport[dev], xirq, xdma1); 287 288 snd_card_set_dev(card, devptr); 289 290 if ((err = snd_card_register(card)) < 0) 291 goto _err; 292 293 dev_set_drvdata(devptr, card); 294 return 0; 295 296 _err: 297 snd_card_free(card); 298 return err; 299} 300 301static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev) 302{ 303 snd_card_free(dev_get_drvdata(devptr)); 304 dev_set_drvdata(devptr, NULL); 305 return 0; 306} 307 308#ifdef CONFIG_PM 309static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n, 310 pm_message_t state) 311{ 312 struct snd_card *card = dev_get_drvdata(pdev); 313 struct snd_wss *chip = card->private_data; 314 315 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 316 chip->suspend(chip); 317 return 0; 318} 319 320static int snd_sgalaxy_resume(struct device *pdev, unsigned int n) 321{ 322 struct snd_card *card = dev_get_drvdata(pdev); 323 struct snd_wss *chip = card->private_data; 324 325 chip->resume(chip); 326 snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]); 327 snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]); 328 329 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 330 return 0; 331} 332#endif 333 334#define DEV_NAME "sgalaxy" 335 336static struct isa_driver snd_sgalaxy_driver = { 337 .match = snd_sgalaxy_match, 338 .probe = snd_sgalaxy_probe, 339 .remove = __devexit_p(snd_sgalaxy_remove), 340#ifdef CONFIG_PM 341 .suspend = snd_sgalaxy_suspend, 342 .resume = snd_sgalaxy_resume, 343#endif 344 .driver = { 345 .name = DEV_NAME 346 }, 347}; 348 349static int __init alsa_card_sgalaxy_init(void) 350{ 351 return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS); 352} 353 354static void __exit alsa_card_sgalaxy_exit(void) 355{ 356 isa_unregister_driver(&snd_sgalaxy_driver); 357} 358 359module_init(alsa_card_sgalaxy_init) 360module_exit(alsa_card_sgalaxy_exit) 361