Blame view

sound/isa/sgalaxy.c 9.88 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   *  Driver for Aztech Sound Galaxy cards
   *  Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
   *
   *  I don't have documentation for this card, I based this driver on the
   *  driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  #include <linux/init.h>
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
24
  #include <linux/err.h>
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
25
  #include <linux/isa.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
29
  #include <linux/delay.h>
  #include <linux/time.h>
  #include <linux/interrupt.h>
  #include <linux/moduleparam.h>
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
30
  #include <asm/dma.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
  #include <sound/core.h>
  #include <sound/sb.h>
760fc6b83   Krzysztof Helt   ALSA: wss_lib: us...
33
  #include <sound/wss.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  #include <sound/control.h>
  #define SNDRV_LEGACY_FIND_FREE_IRQ
  #define SNDRV_LEGACY_FIND_FREE_DMA
  #include <sound/initval.h>
  
  MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>");
  MODULE_DESCRIPTION("Aztech Sound Galaxy");
  MODULE_LICENSE("GPL");
  MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}");
  
  static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
  static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
  static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
  static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240 */
  static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x530,0xe80,0xf40,0x604 */
  static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 7,9,10,11 */
  static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
  
  module_param_array(index, int, NULL, 0444);
  MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard.");
  module_param_array(id, charp, NULL, 0444);
  MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard.");
  module_param_array(sbport, long, NULL, 0444);
  MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver.");
  module_param_array(wssport, long, NULL, 0444);
  MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver.");
  module_param_array(irq, int, NULL, 0444);
  MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver.");
  module_param_array(dma1, int, NULL, 0444);
  MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver.");
  
  #define SGALAXY_AUXC_LEFT 18
  #define SGALAXY_AUXC_RIGHT 19
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
67
  #define PFX	"sgalaxy: "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
  /*
  
   */
  
  #define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
11ff5c62b   Takashi Iwai   [ALSA] Remove xxx...
73
  /* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  /* short time we actually need it.. */
  
  static int snd_sgalaxy_sbdsp_reset(unsigned long port)
  {
  	int i;
  
  	outb(1, SBP1(port, RESET));
  	udelay(10);
  	outb(0, SBP1(port, RESET));
  	udelay(30);
  	for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
  	if (inb(SBP1(port, READ)) != 0xaa) {
  		snd_printd("sb_reset: failed at 0x%lx!!!
  ", port);
  		return -ENODEV;
  	}
  	return 0;
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
92
93
  static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port,
  					       unsigned char val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
101
102
103
104
  {
  	int i;
         	
  	for (i = 10000; i; i--)
  		if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
  			outb(val, SBP1(port, COMMAND));
  			return 1;
  		}
  
  	return 0;
  }
7d12e780e   David Howells   IRQ: Maintain reg...
105
  static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
  {
  	return IRQ_NONE;
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
109
  static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  {
  	static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 
  				       0x10, 0x18, 0x20, -1, -1, -1, -1};
  	static int dma_bits[] = {1, 2, 0, 3};
  	int tmp, tmp1;
  
  	if ((tmp = inb(port + 3)) == 0xff)
  	{
  		snd_printdd("I/O address dead (0x%lx)
  ", port);
  		return 0;
  	}
  #if 0
  	snd_printdd("WSS signature = 0x%x
  ", tmp);
  #endif
  
          if ((tmp & 0x3f) != 0x04 &&
              (tmp & 0x3f) != 0x0f &&
              (tmp & 0x3f) != 0x00) {
  		snd_printdd("No WSS signature detected on port 0x%lx
  ",
  			    port + 3);
  		return 0;
  	}
  
  #if 0
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
137
138
  	snd_printdd(PFX "setting up IRQ/DMA for WSS
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
142
143
144
  #endif
  
          /* initialize IRQ for WSS codec */
          tmp = interrupt_bits[irq % 16];
          if (tmp < 0)
                  return -EINVAL;
65ca68b30   Thomas Gleixner   [PATCH] irq-flags...
145
  	if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
151
152
153
154
155
156
157
158
  		snd_printk(KERN_ERR "sgalaxy: can't grab irq %d
  ", irq);
  		return -EIO;
  	}
  
          outb(tmp | 0x40, port);
          tmp1 = dma_bits[dma % 4];
          outb(tmp | tmp1, port);
  
  	free_irq(irq, NULL);
  
  	return 0;
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
159
  static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  {
  #if 0
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
162
163
  	snd_printdd(PFX "switching to WSS mode
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
172
173
174
  #endif
  
  	/* switch to WSS mode */
  	snd_sgalaxy_sbdsp_reset(sbport[dev]);
  
  	snd_sgalaxy_sbdsp_command(sbport[dev], 9);
  	snd_sgalaxy_sbdsp_command(sbport[dev], 0);
  
  	udelay(400);
  	return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
  }
0c5e3e982   Krzysztof Helt   ALSA: wss_lib: re...
175
176
177
178
179
  static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
  WSS_DOUBLE("Aux Playback Switch", 0,
  		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
  WSS_DOUBLE("Aux Playback Volume", 0,
  		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  };
241b3ee70   Krzysztof Helt   ALSA: wss_lib: us...
181
  static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  {
11ff5c62b   Takashi Iwai   [ALSA] Remove xxx...
183
184
  	struct snd_card *card = chip->card;
  	struct snd_ctl_elem_id id1, id2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
  	unsigned int idx;
  	int err;
  
  	memset(&id1, 0, sizeof(id1));
  	memset(&id2, 0, sizeof(id2));
  	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
  	/* reassign AUX0 to LINE */
  	strcpy(id1.name, "Aux Playback Switch");
  	strcpy(id2.name, "Line Playback Switch");
  	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
  		return err;
  	strcpy(id1.name, "Aux Playback Volume");
  	strcpy(id2.name, "Line Playback Volume");
  	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
  		return err;
  	/* reassign AUX1 to FM */
  	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
  	strcpy(id2.name, "FM Playback Switch");
  	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
  		return err;
  	strcpy(id1.name, "Aux Playback Volume");
  	strcpy(id2.name, "FM Playback Volume");
  	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
  		return err;
  	/* build AUX2 input */
  	for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
0c5e3e982   Krzysztof Helt   ALSA: wss_lib: re...
211
212
213
  		err = snd_ctl_add(card,
  				snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
  		if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
  			return err;
  	}
  	return 0;
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
218
  static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
220
221
  	if (!enable[dev])
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  	if (sbport[dev] == SNDRV_AUTO_PORT) {
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
223
224
  		snd_printk(KERN_ERR PFX "specify SB port
  ");
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
225
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
  	}
  	if (wssport[dev] == SNDRV_AUTO_PORT) {
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
228
229
  		snd_printk(KERN_ERR PFX "specify WSS port
  ");
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
230
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
  	}
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
232
233
234
235
236
237
238
239
240
  	return 1;
  }
  
  static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
  {
  	static int possible_irqs[] = {7, 9, 10, 11, -1};
  	static int possible_dmas[] = {1, 3, 0, -1};
  	int err, xirq, xdma1;
  	struct snd_card *card;
241b3ee70   Krzysztof Helt   ALSA: wss_lib: us...
241
  	struct snd_wss *chip;
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
242

c95eadd2f   Takashi Iwai   ALSA: Convert to ...
243
244
245
  	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
  
  	xirq = irq[dev];
  	if (xirq == SNDRV_AUTO_IRQ) {
  		if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
250
251
252
253
  			snd_printk(KERN_ERR PFX "unable to find a free IRQ
  ");
  			err = -EBUSY;
  			goto _err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
258
  		}
  	}
  	xdma1 = dma1[dev];
          if (xdma1 == SNDRV_AUTO_DMA) {
  		if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
259
260
261
262
  			snd_printk(KERN_ERR PFX "unable to find a free DMA
  ");
  			err = -EBUSY;
  			goto _err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
  		}
  	}
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
265
266
  	if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
  		goto _err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267

760fc6b83   Krzysztof Helt   ALSA: wss_lib: us...
268
269
270
271
  	err = snd_wss_create(card, wssport[dev] + 4, -1,
  			     xirq, xdma1, -1,
  			     WSS_HW_DETECT, 0, &chip);
  	if (err < 0)
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
272
  		goto _err;
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
273
  	card->private_data = chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274

ead893c0d   Krzysztof Helt   ALSA: wss_lib: us...
275
276
277
278
  	err = snd_wss_pcm(chip, 0, NULL);
  	if (err < 0) {
  		snd_printdd(PFX "error creating new WSS PCM device
  ");
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
279
  		goto _err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	}
5664daa1c   Krzysztof Helt   ALSA: wss_lib: us...
281
282
283
284
  	err = snd_wss_mixer(chip);
  	if (err < 0) {
  		snd_printdd(PFX "error creating new WSS mixer
  ");
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
285
  		goto _err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  	}
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
287
288
289
290
  	if ((err = snd_sgalaxy_mixer(chip)) < 0) {
  		snd_printdd(PFX "the mixer rewrite failed
  ");
  		goto _err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
  	}
  
  	strcpy(card->driver, "Sound Galaxy");
  	strcpy(card->shortname, "Sound Galaxy");
  	sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
  		wssport[dev], xirq, xdma1);
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
297
  	snd_card_set_dev(card, devptr);
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
298
299
300
  
  	if ((err = snd_card_register(card)) < 0)
  		goto _err;
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
301
  	dev_set_drvdata(devptr, card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  	return 0;
43bcd973d   Takashi Iwai   [ALSA] Add snd_ca...
303
304
305
306
  
   _err:
  	snd_card_free(card);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
308
  static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev)
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
309
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
310
311
  	snd_card_free(dev_get_drvdata(devptr));
  	dev_set_drvdata(devptr, NULL);
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
312
313
314
315
  	return 0;
  }
  
  #ifdef CONFIG_PM
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
316
317
  static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
  			       pm_message_t state)
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
318
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
319
  	struct snd_card *card = dev_get_drvdata(pdev);
241b3ee70   Krzysztof Helt   ALSA: wss_lib: us...
320
  	struct snd_wss *chip = card->private_data;
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
321
322
323
324
325
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
  	chip->suspend(chip);
  	return 0;
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
326
  static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
327
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
328
  	struct snd_card *card = dev_get_drvdata(pdev);
241b3ee70   Krzysztof Helt   ALSA: wss_lib: us...
329
  	struct snd_wss *chip = card->private_data;
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
330
331
  
  	chip->resume(chip);
760fc6b83   Krzysztof Helt   ALSA: wss_lib: us...
332
333
  	snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
  	snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
334
335
336
337
338
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
  	return 0;
  }
  #endif
83c51c0ab   Rene Herman   [ALSA] isa_bus de...
339
  #define DEV_NAME "sgalaxy"
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
340

5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
341
342
  static struct isa_driver snd_sgalaxy_driver = {
  	.match		= snd_sgalaxy_match,
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
343
344
345
346
347
348
349
  	.probe		= snd_sgalaxy_probe,
  	.remove		= __devexit_p(snd_sgalaxy_remove),
  #ifdef CONFIG_PM
  	.suspend	= snd_sgalaxy_suspend,
  	.resume		= snd_sgalaxy_resume,
  #endif
  	.driver		= {
83c51c0ab   Rene Herman   [ALSA] isa_bus de...
350
  		.name	= DEV_NAME
feb158e6a   Takashi Iwai   [ALSA] sgalaxy - ...
351
352
  	},
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
  static int __init alsa_card_sgalaxy_init(void)
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
355
  	return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
  }
  
  static void __exit alsa_card_sgalaxy_exit(void)
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
360
  	isa_unregister_driver(&snd_sgalaxy_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
  }
  
  module_init(alsa_card_sgalaxy_init)
  module_exit(alsa_card_sgalaxy_exit)