Blame view

sound/isa/sscape.c 32.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
acd471009   Krzysztof Helt   ALSA: sscape: con...
2
   *   Low-level ALSA driver for the ENSONIQ SoundScape
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   *   Copyright (c) by Chris Rankin
   *
   *   This driver was written in part using information obtained from
   *   the OSS/Free SoundScape driver, written by Hannu Savolainen.
   *
   *
   *   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>
277e926c9   Takashi Iwai   [ALSA] sscape - U...
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
  #include <linux/delay.h>
acd471009   Krzysztof Helt   ALSA: sscape: con...
27
  #include <linux/firmware.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
  #include <linux/pnp.h>
  #include <linux/spinlock.h>
65a772172   Paul Gortmaker   sound: fix driver...
30
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
  #include <asm/dma.h>
  #include <sound/core.h>
61ef19d7e   Krzysztof Helt   ALSA: wss_lib: re...
33
  #include <sound/wss.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
  #include <sound/mpu401.h>
  #include <sound/initval.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  
  MODULE_AUTHOR("Chris Rankin");
acd471009   Krzysztof Helt   ALSA: sscape: con...
38
  MODULE_DESCRIPTION("ENSONIQ SoundScape driver");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  MODULE_LICENSE("GPL");
acd471009   Krzysztof Helt   ALSA: sscape: con...
40
41
42
43
44
45
  MODULE_FIRMWARE("sndscape.co0");
  MODULE_FIRMWARE("sndscape.co1");
  MODULE_FIRMWARE("sndscape.co2");
  MODULE_FIRMWARE("sndscape.co3");
  MODULE_FIRMWARE("sndscape.co4");
  MODULE_FIRMWARE("scope.cod");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

ed76f652d   Takashi Iwai   ALSA: sscape - Re...
47
48
49
50
51
52
53
54
55
  static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
  static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
  static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
  static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
  static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
  static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
  static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
  static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
  static bool joystick[SNDRV_CARDS];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
64
  
  module_param_array(index, int, NULL, 0444);
  MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
  
  module_param_array(id, charp, NULL, 0444);
  MODULE_PARM_DESC(id, "Description for SoundScape card");
  
  module_param_array(port, long, NULL, 0444);
  MODULE_PARM_DESC(port, "Port # for SoundScape driver.");
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
65
66
  module_param_array(wss_port, long, NULL, 0444);
  MODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
72
73
74
  module_param_array(irq, int, NULL, 0444);
  MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");
  
  module_param_array(mpu_irq, int, NULL, 0444);
  MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");
  
  module_param_array(dma, int, NULL, 0444);
  MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
f7a9275d9   Clemens Ladisch   [ALSA] unregister...
75

4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
76
77
  module_param_array(dma2, int, NULL, 0444);
  MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
78
79
  module_param_array(joystick, bool, NULL, 0444);
  MODULE_PARM_DESC(joystick, "Enable gameport.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  #ifdef CONFIG_PNP
609d76941   Rene Herman   [ALSA] Fix probe ...
81
  static int isa_registered;
59b1b34f4   Takashi Iwai   [ALSA] Fix compil...
82
  static int pnp_registered;
609d76941   Rene Herman   [ALSA] Fix probe ...
83

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  static struct pnp_card_device_id sscape_pnpids[] = {
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
85
86
  	{ .id = "ENS3081", .devs = { { "ENS0000" } } }, /* Soundscape PnP */
  	{ .id = "ENS4081", .devs = { { "ENS1011" } } },	/* VIVO90 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
90
91
  	{ .id = "" }	/* end */
  };
  
  MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
102
103
  #define HOST_CTRL_IO(i)  ((i) + 2)
  #define HOST_DATA_IO(i)  ((i) + 3)
  #define ODIE_ADDR_IO(i)  ((i) + 4)
  #define ODIE_DATA_IO(i)  ((i) + 5)
  #define CODEC_IO(i)      ((i) + 8)
  
  #define IC_ODIE  1
  #define IC_OPUS  2
  
  #define RX_READY 0x01
  #define TX_READY 0x02
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
104
105
106
107
108
109
110
111
  #define CMD_ACK			0x80
  #define CMD_SET_MIDI_VOL	0x84
  #define CMD_GET_MIDI_VOL	0x85
  #define CMD_XXX_MIDI_VOL	0x86
  #define CMD_SET_EXTMIDI		0x8a
  #define CMD_GET_EXTMIDI		0x8b
  #define CMD_SET_MT32		0x8c
  #define CMD_GET_MT32		0x8d
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  
  enum GA_REG {
  	GA_INTSTAT_REG = 0,
  	GA_INTENA_REG,
  	GA_DMAA_REG,
  	GA_DMAB_REG,
  	GA_INTCFG_REG,
  	GA_DMACFG_REG,
  	GA_CDCFG_REG,
  	GA_SMCFGA_REG,
  	GA_SMCFGB_REG,
  	GA_HMCTL_REG
  };
  
  #define DMA_8BIT  0x80
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
127
  enum card_type {
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
128
129
  	MEDIA_FX,	/* Sequoia S-1000 */
  	SSCAPE,		/* Sequoia S-2000 */
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
130
131
132
  	SSCAPE_PNP,
  	SSCAPE_VIVO,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
  struct soundscape {
  	spinlock_t lock;
  	unsigned io_base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  	int ic_type;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
137
  	enum card_type type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  	struct resource *io_res;
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
139
  	struct resource *wss_res;
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
140
  	struct snd_wss *chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
  	unsigned char midi_vol;
  };
  
  #define INVALID_IRQ  ((unsigned)-1)
be6245373   Takashi Iwai   [ALSA] Remove xxx...
146
  static inline struct soundscape *get_card_soundscape(struct snd_card *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
  {
  	return (struct soundscape *) (c->private_data);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
154
  /*
   * Allocates some kernel memory that we can use for DMA.
   * I think this means that the memory has to map to
   * contiguous pages of physical memory.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
155
156
  static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf,
  					 unsigned long size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
  {
  	if (buf) {
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
159
160
  		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
  						 snd_dma_isa_data(),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  						 size, buf) < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
162
163
164
165
  			snd_printk(KERN_ERR "sscape: Failed to allocate "
  					    "%lu bytes for DMA
  ",
  					    size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  			return NULL;
  		}
  	}
  
  	return buf;
  }
  
  /*
   * Release the DMA-able kernel memory ...
   */
  static void free_dmabuf(struct snd_dma_buffer *buf)
  {
  	if (buf && buf->area)
  		snd_dma_free_pages(buf);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
  /*
   * This function writes to the SoundScape's control registers,
   * but doesn't do any locking. It's up to the caller to do that.
   * This is why this function is "unsafe" ...
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
186
187
  static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
  				       unsigned char val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
  {
  	outb(reg, ODIE_ADDR_IO(io_base));
  	outb(val, ODIE_DATA_IO(io_base));
  }
  
  /*
   * Write to the SoundScape's control registers, and do the
   * necessary locking ...
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
197
198
  static void sscape_write(struct soundscape *s, enum GA_REG reg,
  			 unsigned char val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
203
204
205
206
207
208
209
210
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&s->lock, flags);
  	sscape_write_unsafe(s->io_base, reg, val);
  	spin_unlock_irqrestore(&s->lock, flags);
  }
  
  /*
   * Read from the SoundScape's control registers, but leave any
   * locking to the caller. This is why the function is "unsafe" ...
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
211
212
  static inline unsigned char sscape_read_unsafe(unsigned io_base,
  					       enum GA_REG reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  {
  	outb(reg, ODIE_ADDR_IO(io_base));
  	return inb(ODIE_DATA_IO(io_base));
  }
  
  /*
   * Puts the SoundScape into "host" mode, as compared to "MIDI" mode
   */
  static inline void set_host_mode_unsafe(unsigned io_base)
  {
  	outb(0x0, HOST_CTRL_IO(io_base));
  }
  
  /*
   * Puts the SoundScape into "MIDI" mode, as compared to "host" mode
   */
  static inline void set_midi_mode_unsafe(unsigned io_base)
  {
  	outb(0x3, HOST_CTRL_IO(io_base));
  }
  
  /*
   * Read the SoundScape's host-mode control register, but leave
   * any locking issues to the caller ...
   */
  static inline int host_read_unsafe(unsigned io_base)
  {
  	int data = -1;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
241
  	if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
  		data = inb(HOST_DATA_IO(io_base));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  
  	return data;
  }
  
  /*
   * Read the SoundScape's host-mode control register, performing
   * a limited amount of busy-waiting if the register isn't ready.
   * Also leaves all locking-issues to the caller ...
   */
  static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout)
  {
  	int data;
  
  	while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) {
  		udelay(100);
  		--timeout;
  	} /* while */
  
  	return data;
  }
  
  /*
   * Write to the SoundScape's host-mode control registers, but
   * leave any locking issues to the caller ...
   */
  static inline int host_write_unsafe(unsigned io_base, unsigned char data)
  {
  	if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) {
  		outb(data, HOST_DATA_IO(io_base));
  		return 1;
  	}
  
  	return 0;
  }
  
  /*
   * Write to the SoundScape's host-mode control registers, performing
   * a limited amount of busy-waiting if the register isn't ready.
   * Also leaves all locking-issues to the caller ...
   */
  static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
284
  				  unsigned timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  {
  	int err;
  
  	while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) {
  		udelay(100);
  		--timeout;
  	} /* while */
  
  	return err;
  }
  
  
  /*
   * Check that the MIDI subsystem is operational. If it isn't,
   * then we will hang the computer if we try to use it ...
   *
   * NOTE: This check is based upon observation, not documentation.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
303
  static inline int verify_mpu401(const struct snd_mpu401 *mpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  {
c9864fd30   Krzysztof Helt   ALSA: sscape: use...
305
  	return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
310
  }
  
  /*
   * This is apparently the standard way to initailise an MPU-401
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
311
  static inline void initialise_mpu401(const struct snd_mpu401 *mpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  {
c9864fd30   Krzysztof Helt   ALSA: sscape: use...
313
  	outb(0, MPU401D(mpu));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
320
  }
  
  /*
   * Tell the SoundScape to activate the AD1845 chip (I think).
   * The AD1845 detection fails if we *don't* do this, so I
   * think that this is a good idea ...
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
321
  static void activate_ad1845_unsafe(unsigned io_base)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
  {
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
323
324
  	unsigned char val = sscape_read_unsafe(io_base, GA_HMCTL_REG);
  	sscape_write_unsafe(io_base, GA_HMCTL_REG, (val & 0xcf) | 0x10);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
328
329
330
  	sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
  }
  
  /*
   * Do the necessary ALSA-level cleanup to deallocate our driver ...
   */
be6245373   Takashi Iwai   [ALSA] Remove xxx...
331
  static void soundscape_free(struct snd_card *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  {
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
333
  	struct soundscape *sscape = get_card_soundscape(c);
b1d5776d8   Takashi Iwai   [ALSA] Remove vma...
334
  	release_and_free_resource(sscape->io_res);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
335
  	release_and_free_resource(sscape->wss_res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
  	free_dma(sscape->chip->dma1);
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
   * Tell the SoundScape to begin a DMA tranfer using the given channel.
   * All locking issues are left to the caller.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
343
  static void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
  {
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
345
346
347
348
  	sscape_write_unsafe(io_base, reg,
  			    sscape_read_unsafe(io_base, reg) | 0x01);
  	sscape_write_unsafe(io_base, reg,
  			    sscape_read_unsafe(io_base, reg) & 0xfe);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
351
352
353
354
  }
  
  /*
   * Wait for a DMA transfer to complete. This is a "limited busy-wait",
   * and all locking issues are left to the caller.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
355
356
  static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg,
  				  unsigned timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
  {
  	while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
  		udelay(100);
  		--timeout;
  	} /* while */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
362
  	return sscape_read_unsafe(io_base, reg) & 0x01;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
367
368
369
370
371
372
373
  }
  
  /*
   * Wait for the On-Board Processor to return its start-up
   * acknowledgement sequence. This wait is too long for
   * us to perform "busy-waiting", and so we must sleep.
   * This in turn means that we must not be holding any
   * spinlocks when we call this function.
   */
  static int obp_startup_ack(struct soundscape *s, unsigned timeout)
  {
554b91ede   Krzysztof Helt   ALSA: sscape: fix...
374
375
376
  	unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
  
  	do {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  		unsigned long flags;
acd471009   Krzysztof Helt   ALSA: sscape: con...
378
  		int x;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  		spin_lock_irqsave(&s->lock, flags);
acd471009   Krzysztof Helt   ALSA: sscape: con...
381
  		x = host_read_unsafe(s->io_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  		spin_unlock_irqrestore(&s->lock, flags);
acd471009   Krzysztof Helt   ALSA: sscape: con...
383
  		if (x == 0xfe || x == 0xff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  			return 1;
554b91ede   Krzysztof Helt   ALSA: sscape: fix...
385
386
  		msleep(10);
  	} while (time_before(jiffies, end_time));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
391
392
393
394
395
396
397
398
399
  
  	return 0;
  }
  
  /*
   * Wait for the host to return its start-up acknowledgement
   * sequence. This wait is too long for us to perform
   * "busy-waiting", and so we must sleep. This in turn means
   * that we must not be holding any spinlocks when we call
   * this function.
   */
  static int host_startup_ack(struct soundscape *s, unsigned timeout)
  {
554b91ede   Krzysztof Helt   ALSA: sscape: fix...
400
401
402
  	unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
  
  	do {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  		unsigned long flags;
acd471009   Krzysztof Helt   ALSA: sscape: con...
404
  		int x;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  		spin_lock_irqsave(&s->lock, flags);
acd471009   Krzysztof Helt   ALSA: sscape: con...
407
  		x = host_read_unsafe(s->io_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
410
  		spin_unlock_irqrestore(&s->lock, flags);
  		if (x == 0xfe)
  			return 1;
554b91ede   Krzysztof Helt   ALSA: sscape: fix...
411
412
  		msleep(10);
  	} while (time_before(jiffies, end_time));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
418
419
  
  	return 0;
  }
  
  /*
   * Upload a byte-stream into the SoundScape using DMA channel A.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
420
421
  static int upload_dma_data(struct soundscape *s, const unsigned char *data,
  			   size_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
  {
  	unsigned long flags;
  	struct snd_dma_buffer dma;
  	int ret;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
426
  	unsigned char val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427

acd471009   Krzysztof Helt   ALSA: sscape: con...
428
  	if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
434
435
  		return -ENOMEM;
  
  	spin_lock_irqsave(&s->lock, flags);
  
  	/*
  	 * Reset the board ...
  	 */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
436
437
  	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
  	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
  
  	/*
  	 * Enable the DMA channels and configure them ...
  	 */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
442
443
  	val = (s->chip->dma1 << 4) | DMA_8BIT;
  	sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
446
447
448
  	sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
  
  	/*
  	 * Take the board out of reset ...
  	 */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
449
450
  	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
  	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
  
  	/*
acd471009   Krzysztof Helt   ALSA: sscape: con...
453
  	 * Upload the firmware to the SoundScape
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
  	 * board through the DMA channel ...
  	 */
  	while (size != 0) {
  		unsigned long len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  		len = min(size, dma.bytes);
acd471009   Krzysztof Helt   ALSA: sscape: con...
459
  		memcpy(dma.area, data, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
  		data += len;
  		size -= len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
  		snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
  		sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
  		if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
  			/*
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
466
  			 * Don't forget to release this spinlock we're holding
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
  			 */
  			spin_unlock_irqrestore(&s->lock, flags);
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
469
470
471
  			snd_printk(KERN_ERR
  					"sscape: DMA upload has timed out
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
477
  			ret = -EAGAIN;
  			goto _release_dma;
  		}
  	} /* while */
  
  	set_host_mode_unsafe(s->io_base);
acd471009   Krzysztof Helt   ALSA: sscape: con...
478
  	outb(0x0, s->io_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
481
482
  
  	/*
  	 * Boot the board ... (I think)
  	 */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
483
484
  	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
  	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
488
489
490
491
492
  	spin_unlock_irqrestore(&s->lock, flags);
  
  	/*
  	 * If all has gone well, then the board should acknowledge
  	 * the new upload and tell us that it has rebooted OK. We
  	 * give it 5 seconds (max) ...
  	 */
  	ret = 0;
554b91ede   Krzysztof Helt   ALSA: sscape: fix...
493
  	if (!obp_startup_ack(s, 5000)) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
494
495
496
  		snd_printk(KERN_ERR "sscape: No response "
  				    "from on-board processor after upload
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  		ret = -EAGAIN;
554b91ede   Krzysztof Helt   ALSA: sscape: fix...
498
  	} else if (!host_startup_ack(s, 5000)) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
499
500
501
  		snd_printk(KERN_ERR
  				"sscape: SoundScape failed to initialise
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
  		ret = -EAGAIN;
  	}
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
504
  _release_dma:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
  	/*
  	 * NOTE!!! We are NOT holding any spinlocks at this point !!!
  	 */
acd471009   Krzysztof Helt   ALSA: sscape: con...
508
  	sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
511
512
513
514
515
516
517
  	free_dmabuf(&dma);
  
  	return ret;
  }
  
  /*
   * Upload the bootblock(?) into the SoundScape. The only
   * purpose of this block of code seems to be to tell
   * us which version of the microcode we should be using.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
   */
acd471009   Krzysztof Helt   ALSA: sscape: con...
519
  static int sscape_upload_bootblock(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
  {
acd471009   Krzysztof Helt   ALSA: sscape: con...
521
  	struct soundscape *sscape = get_card_soundscape(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
  	unsigned long flags;
acd471009   Krzysztof Helt   ALSA: sscape: con...
523
  	const struct firmware *init_fw = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
  	int data = 0;
  	int ret;
acd471009   Krzysztof Helt   ALSA: sscape: con...
526
527
  	ret = request_firmware(&init_fw, "scope.cod", card->dev);
  	if (ret < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
528
  		snd_printk(KERN_ERR "sscape: Error loading scope.cod");
acd471009   Krzysztof Helt   ALSA: sscape: con...
529
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  	}
acd471009   Krzysztof Helt   ALSA: sscape: con...
531
  	ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532

acd471009   Krzysztof Helt   ALSA: sscape: con...
533
  	release_firmware(init_fw);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534

acd471009   Krzysztof Helt   ALSA: sscape: con...
535
536
537
  	spin_lock_irqsave(&sscape->lock, flags);
  	if (ret == 0)
  		data = host_read_ctrl_unsafe(sscape->io_base, 100);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

acd471009   Krzysztof Helt   ALSA: sscape: con...
539
540
  	if (data & 0x10)
  		sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
  	spin_unlock_irqrestore(&sscape->lock, flags);
acd471009   Krzysztof Helt   ALSA: sscape: con...
543
544
  	data &= 0xf;
  	if (ret == 0 && data > 7) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
545
546
547
  		snd_printk(KERN_ERR
  				"sscape: timeout reading firmware version
  ");
acd471009   Krzysztof Helt   ALSA: sscape: con...
548
549
  		ret = -EAGAIN;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550

acd471009   Krzysztof Helt   ALSA: sscape: con...
551
  	return (ret == 0) ? data : ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
  }
  
  /*
acd471009   Krzysztof Helt   ALSA: sscape: con...
555
   * Upload the microcode into the SoundScape.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
   */
acd471009   Krzysztof Helt   ALSA: sscape: con...
557
  static int sscape_upload_microcode(struct snd_card *card, int version)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  {
acd471009   Krzysztof Helt   ALSA: sscape: con...
559
560
561
  	struct soundscape *sscape = get_card_soundscape(card);
  	const struct firmware *init_fw = NULL;
  	char name[14];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  	int err;
acd471009   Krzysztof Helt   ALSA: sscape: con...
563
  	snprintf(name, sizeof(name), "sndscape.co%d", version);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564

acd471009   Krzysztof Helt   ALSA: sscape: con...
565
566
  	err = request_firmware(&init_fw, name, card->dev);
  	if (err < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
567
568
  		snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
  				version);
acd471009   Krzysztof Helt   ALSA: sscape: con...
569
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
  	}
acd471009   Krzysztof Helt   ALSA: sscape: con...
571
572
  	err = upload_dma_data(sscape, init_fw->data, init_fw->size);
  	if (err == 0)
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
573
574
  		snd_printk(KERN_INFO "sscape: MIDI firmware loaded %d KBs
  ",
acd471009   Krzysztof Helt   ALSA: sscape: con...
575
  				init_fw->size >> 10);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576

acd471009   Krzysztof Helt   ALSA: sscape: con...
577
  	release_firmware(init_fw);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
  
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
583
  /*
   * Mixer control for the SoundScape's MIDI device.
   */
be6245373   Takashi Iwai   [ALSA] Remove xxx...
584
  static int sscape_midi_info(struct snd_kcontrol *ctl,
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
585
  			    struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
589
590
591
592
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 127;
  	return 0;
  }
be6245373   Takashi Iwai   [ALSA] Remove xxx...
593
  static int sscape_midi_get(struct snd_kcontrol *kctl,
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
594
  			   struct snd_ctl_elem_value *uctl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  {
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
596
  	struct snd_wss *chip = snd_kcontrol_chip(kctl);
be6245373   Takashi Iwai   [ALSA] Remove xxx...
597
  	struct snd_card *card = chip->card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
  	register struct soundscape *s = get_card_soundscape(card);
  	unsigned long flags;
  
  	spin_lock_irqsave(&s->lock, flags);
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
602
  	uctl->value.integer.value[0] = s->midi_vol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
  	spin_unlock_irqrestore(&s->lock, flags);
  	return 0;
  }
be6245373   Takashi Iwai   [ALSA] Remove xxx...
606
  static int sscape_midi_put(struct snd_kcontrol *kctl,
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
607
  			   struct snd_ctl_elem_value *uctl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  {
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
609
  	struct snd_wss *chip = snd_kcontrol_chip(kctl);
be6245373   Takashi Iwai   [ALSA] Remove xxx...
610
  	struct snd_card *card = chip->card;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
611
  	struct soundscape *s = get_card_soundscape(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
  	unsigned long flags;
  	int change;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
614
  	unsigned char new_val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
  
  	spin_lock_irqsave(&s->lock, flags);
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
617
  	new_val = uctl->value.integer.value[0] & 127;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
620
621
622
623
624
625
626
627
628
629
  	/*
  	 * We need to put the board into HOST mode before we
  	 * can send any volume-changing HOST commands ...
  	 */
  	set_host_mode_unsafe(s->io_base);
  
  	/*
  	 * To successfully change the MIDI volume setting, you seem to
  	 * have to write a volume command, write the new volume value,
  	 * and then perform another volume-related command. Perhaps the
  	 * first command is an "open" and the second command is a "close"?
  	 */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
630
  	if (s->midi_vol == new_val) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
  		change = 0;
  		goto __skip_change;
  	}
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
634
635
636
637
638
639
  	change = host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
  		 && host_write_ctrl_unsafe(s->io_base, new_val, 100)
  		 && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100)
  		 && host_write_ctrl_unsafe(s->io_base, new_val, 100);
  	s->midi_vol = new_val;
  __skip_change:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
646
647
648
  
  	/*
  	 * Take the board out of HOST mode and back into MIDI mode ...
  	 */
  	set_midi_mode_unsafe(s->io_base);
  
  	spin_unlock_irqrestore(&s->lock, flags);
  	return change;
  }
be6245373   Takashi Iwai   [ALSA] Remove xxx...
649
  static struct snd_kcontrol_new midi_mixer_ctl = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
652
653
654
655
656
657
658
659
660
661
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "MIDI",
  	.info = sscape_midi_info,
  	.get = sscape_midi_get,
  	.put = sscape_midi_put
  };
  
  /*
   * The SoundScape can use two IRQs from a possible set of four.
   * These IRQs are encoded as bit patterns so that they can be
   * written to the control registers.
   */
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
662
  static unsigned __devinit get_irq_config(int sscape_type, int irq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
  {
  	static const int valid_irq[] = { 9, 5, 7, 10 };
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
665
  	static const int old_irq[] = { 9, 7, 5, 15 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
  	unsigned cfg;
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
667
668
669
670
671
672
673
674
675
  	if (sscape_type == MEDIA_FX) {
  		for (cfg = 0; cfg < ARRAY_SIZE(old_irq); ++cfg)
  			if (irq == old_irq[cfg])
  				return cfg;
  	} else {
  		for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg)
  			if (irq == valid_irq[cfg])
  				return cfg;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
  
  	return INVALID_IRQ;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
681
682
  /*
   * Perform certain arcane port-checks to see whether there
   * is a SoundScape board lurking behind the given ports.
   */
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
683
  static int __devinit detect_sscape(struct soundscape *s, long wss_io)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  {
  	unsigned long flags;
  	unsigned d;
  	int retval = 0;
  
  	spin_lock_irqsave(&s->lock, flags);
  
  	/*
  	 * The following code is lifted from the original OSS driver,
  	 * and as I don't have a datasheet I cannot really comment
  	 * on what it is doing...
  	 */
  	if ((inb(HOST_CTRL_IO(s->io_base)) & 0x78) != 0)
  		goto _done;
  
  	d = inb(ODIE_ADDR_IO(s->io_base)) & 0xf0;
  	if ((d & 0x80) != 0)
  		goto _done;
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
702
  	if (d == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
  		s->ic_type = IC_ODIE;
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
704
  	else if ((d & 0x60) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
  		s->ic_type = IC_OPUS;
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
706
  	else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
711
712
713
714
715
  		goto _done;
  
  	outb(0xfa, ODIE_ADDR_IO(s->io_base));
  	if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0a)
  		goto _done;
  
  	outb(0xfe, ODIE_ADDR_IO(s->io_base));
  	if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0e)
  		goto _done;
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
716
717
718
719
  
  	outb(0xfe, ODIE_ADDR_IO(s->io_base));
  	d = inb(ODIE_DATA_IO(s->io_base));
  	if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
  		goto _done;
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
721
722
  	if (s->ic_type == IC_OPUS)
  		activate_ad1845_unsafe(s->io_base);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
723
724
  
  	if (s->type == SSCAPE_VIVO)
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
725
  		wss_io += 4;
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
726

6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
727
  	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
728
729
730
731
732
733
734
735
736
737
  	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
  
  	/* wait for WSS codec */
  	for (d = 0; d < 500; d++) {
  		if ((inb(wss_io) & 0x80) == 0)
  			break;
  		spin_unlock_irqrestore(&s->lock, flags);
  		msleep(1);
  		spin_lock_irqsave(&s->lock, flags);
  	}
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
738
739
740
741
742
743
744
745
746
747
748
749
  
  	if ((inb(wss_io) & 0x80) != 0)
  		goto _done;
  
  	if (inb(wss_io + 2) == 0xff)
  		goto _done;
  
  	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
  	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d);
  
  	if ((inb(wss_io) & 0x80) != 0)
  		s->type = MEDIA_FX;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
750
  	d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
751
  	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
752
753
  	/* wait for WSS codec */
  	for (d = 0; d < 500; d++) {
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
754
  		if ((inb(wss_io) & 0x80) == 0)
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
755
756
757
758
759
  			break;
  		spin_unlock_irqrestore(&s->lock, flags);
  		msleep(1);
  		spin_lock_irqsave(&s->lock, flags);
  	}
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
760

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
763
764
  	/*
  	 * SoundScape successfully detected!
  	 */
  	retval = 1;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
765
  _done:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
767
768
769
770
771
772
773
774
775
  	spin_unlock_irqrestore(&s->lock, flags);
  	return retval;
  }
  
  /*
   * ALSA callback function, called when attempting to open the MIDI device.
   * Check that the MIDI firmware has been loaded, because we don't want
   * to crash the machine. Also check that someone isn't using the hardware
   * IOCTL device.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
776
  static int mpu401_open(struct snd_mpu401 *mpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
  	if (!verify_mpu401(mpu)) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
779
780
781
782
  		snd_printk(KERN_ERR "sscape: MIDI disabled, "
  				    "please load firmware
  ");
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
  	}
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
784
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
788
789
  }
  
  /*
   * Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
   */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
790
791
  static int __devinit create_mpu401(struct snd_card *card, int devnum,
  				   unsigned long port, int irq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
  {
  	struct soundscape *sscape = get_card_soundscape(card);
be6245373   Takashi Iwai   [ALSA] Remove xxx...
794
  	struct snd_rawmidi *rawmidi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
  	int err;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
796
  	err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
dba8b4699   Clemens Ladisch   ALSA: mpu401: cle...
797
  				  MPU401_INFO_INTEGRATED, irq, &rawmidi);
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
798
799
  	if (err == 0) {
  		struct snd_mpu401 *mpu = rawmidi->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
  		mpu->open_input = mpu401_open;
  		mpu->open_output = mpu401_open;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  		mpu->private_data = sscape;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
805
806
807
808
809
810
811
  
  		initialise_mpu401(mpu);
  	}
  
  	return err;
  }
  
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
815
816
   * Create an AD1845 PCM subdevice on the SoundScape. The AD1845
   * is very much like a CS4231, with a few extra bits. We will
   * try to support at least some of the extra bits by overriding
   * some of the CS4231 callback.
   */
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
817
818
  static int __devinit create_ad1845(struct snd_card *card, unsigned port,
  				   int irq, int dma1, int dma2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
  {
  	register struct soundscape *sscape = get_card_soundscape(card);
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
821
  	struct snd_wss *chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
  	int err;
1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
823
824
825
826
827
828
829
830
831
832
833
834
835
836
  	int codec_type = WSS_HW_DETECT;
  
  	switch (sscape->type) {
  	case MEDIA_FX:
  	case SSCAPE:
  		/*
  		 * There are some freak examples of early Soundscape cards
  		 * with CS4231 instead of AD1848/CS4248. Unfortunately, the
  		 * CS4231 works only in CS4248 compatibility mode on
  		 * these cards so force it.
  		 */
  		if (sscape->ic_type != IC_OPUS)
  			codec_type = WSS_HW_AD1848;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837

1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
838
  	case SSCAPE_VIVO:
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
839
  		port += 4;
1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
840
841
842
843
  		break;
  	default:
  		break;
  	}
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
844

7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
845
  	err = snd_wss_create(card, port, -1, irq, dma1, dma2,
1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
846
  			     codec_type, WSS_HWSHARE_DMA1, &chip);
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
847
  	if (!err) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  		unsigned long flags;
be6245373   Takashi Iwai   [ALSA] Remove xxx...
849
  		struct snd_pcm *pcm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850

ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
851
  		if (sscape->type != SSCAPE_VIVO) {
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
852
853
854
855
856
  			/*
  			 * The input clock frequency on the SoundScape must
  			 * be 14.31818 MHz, because we must set this register
  			 * to get the playback to sound correct ...
  			 */
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
857
  			snd_wss_mce_up(chip);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
858
  			spin_lock_irqsave(&chip->reg_lock, flags);
199f79787   Krzysztof Helt   ALSA: wss-lib: mo...
859
  			snd_wss_out(chip, AD1845_CLOCK, 0x20);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
860
  			spin_unlock_irqrestore(&chip->reg_lock, flags);
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
861
  			snd_wss_mce_down(chip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862

ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
863
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864

7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
865
  		err = snd_wss_pcm(chip, 0, &pcm);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
866
867
868
869
  		if (err < 0) {
  			snd_printk(KERN_ERR "sscape: No PCM device "
  					    "for AD1845 chip
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870
871
  			goto _error;
  		}
7779f75f0   Krzysztof Helt   ALSA: wss_lib: re...
872
  		err = snd_wss_mixer(chip);
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
873
874
875
876
  		if (err < 0) {
  			snd_printk(KERN_ERR "sscape: No mixer device "
  					    "for AD1845 chip
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
878
  			goto _error;
  		}
199f79787   Krzysztof Helt   ALSA: wss-lib: mo...
879
880
881
882
883
884
885
886
  		if (chip->hardware != WSS_HW_AD1848) {
  			err = snd_wss_timer(chip, 0, NULL);
  			if (err < 0) {
  				snd_printk(KERN_ERR "sscape: No timer device "
  						    "for AD1845 chip
  ");
  				goto _error;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  		}
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
888
889
890
891
892
893
894
895
896
  		if (sscape->type != SSCAPE_VIVO) {
  			err = snd_ctl_add(card,
  					  snd_ctl_new1(&midi_mixer_ctl, chip));
  			if (err < 0) {
  				snd_printk(KERN_ERR "sscape: Could not create "
  						    "MIDI mixer control
  ");
  				goto _error;
  			}
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
897
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
899
  		sscape->chip = chip;
  	}
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
900
  _error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903
904
905
906
  /*
   * Create an ALSA soundcard entry for the SoundScape, using
   * the given list of port, IRQ and DMA resources.
   */
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
907
  static int __devinit create_sscape(int dev, struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  {
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
909
910
  	struct soundscape *sscape = get_card_soundscape(card);
  	unsigned dma_cfg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
913
  	unsigned irq_cfg;
  	unsigned mpu_irq_cfg;
  	struct resource *io_res;
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
914
  	struct resource *wss_res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
  	unsigned long flags;
  	int err;
1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
917
  	int val;
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
918
  	const char *name;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
920
921
922
923
  
  	/*
  	 * Grab IO ports that we will need to probe so that we
  	 * can detect and control this hardware ...
  	 */
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
924
  	io_res = request_region(port[dev], 8, "SoundScape");
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
925
  	if (!io_res) {
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
926
927
928
  		snd_printk(KERN_ERR
  			   "sscape: can't grab port 0x%lx
  ", port[dev]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
930
  		return -EBUSY;
  	}
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
931
932
933
934
935
936
937
938
939
940
941
  	wss_res = NULL;
  	if (sscape->type == SSCAPE_VIVO) {
  		wss_res = request_region(wss_port[dev], 4, "SoundScape");
  		if (!wss_res) {
  			snd_printk(KERN_ERR "sscape: can't grab port 0x%lx
  ",
  					    wss_port[dev]);
  			err = -EBUSY;
  			goto _release_region;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
  
  	/*
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
944
  	 * Grab one DMA channel ...
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
  	 */
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
946
947
  	err = request_dma(dma[dev], "SoundScape");
  	if (err < 0) {
277e926c9   Takashi Iwai   [ALSA] sscape - U...
948
949
  		snd_printk(KERN_ERR "sscape: can't grab DMA %d
  ", dma[dev]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
951
  		goto _release_region;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
  	spin_lock_init(&sscape->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953
  	sscape->io_res = io_res;
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
954
  	sscape->wss_res = wss_res;
453e37b37   Krzysztof Helt   ALSA: sscape: dro...
955
  	sscape->io_base = port[dev];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956

453e37b37   Krzysztof Helt   ALSA: sscape: dro...
957
  	if (!detect_sscape(sscape, wss_port[dev])) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
958
959
960
  		printk(KERN_ERR "sscape: hardware not detected at 0x%x
  ",
  			sscape->io_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
  		err = -ENODEV;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
962
  		goto _release_dma;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
  	}
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
  	switch (sscape->type) {
  	case MEDIA_FX:
  		name = "MediaFX/SoundFX";
  		break;
  	case SSCAPE:
  		name = "Soundscape";
  		break;
  	case SSCAPE_PNP:
  		name = "Soundscape PnP";
  		break;
  	case SSCAPE_VIVO:
  		name = "Soundscape VIVO";
  		break;
  	default:
  		name = "unknown Soundscape";
  		break;
  	}
  
  	printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d
  ",
  			 name, sscape->io_base, irq[dev], dma[dev]);
  
  	/*
  	 * Check that the user didn't pass us garbage data ...
  	 */
  	irq_cfg = get_irq_config(sscape->type, irq[dev]);
  	if (irq_cfg == INVALID_IRQ) {
  		snd_printk(KERN_ERR "sscape: Invalid IRQ %d
  ", irq[dev]);
  		return -ENXIO;
  	}
  
  	mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
  	if (mpu_irq_cfg == INVALID_IRQ) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
998
999
  		snd_printk(KERN_ERR "sscape: Invalid IRQ %d
  ", mpu_irq[dev]);
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
1000
1001
  		return -ENXIO;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
1007
  	/*
  	 * Tell the on-board devices where their resources are (I think -
  	 * I can't be sure without a datasheet ... So many magic values!)
  	 */
  	spin_lock_irqsave(&sscape->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
1009
1010
1011
1012
1013
1014
  	sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
  	sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
  
  	/*
  	 * Enable and configure the DMA channels ...
  	 */
  	sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
1015
  	dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
1017
  	sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
  	sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
1018
  	mpu_irq_cfg |= mpu_irq_cfg << 2;
1cb0fdeba   Krzysztof Helt   ALSA: sscape: for...
1019
1020
1021
1022
  	val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
  	if (joystick[dev])
  		val |= 8;
  	sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
f0968e3f7   Krzysztof Helt   ALSA: sscape: add...
1023
  	sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1024
  	sscape_write_unsafe(sscape->io_base,
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1025
1026
  			    GA_CDCFG_REG, 0x09 | DMA_8BIT
  			    | (dma[dev] << 4) | (irq_cfg << 1));
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
1027
1028
1029
1030
  	/*
  	 * Enable the master IRQ ...
  	 */
  	sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
1034
1035
1036
1037
  
  	spin_unlock_irqrestore(&sscape->lock, flags);
  
  	/*
  	 * We have now enabled the codec chip, and so we should
  	 * detect the AD1845 device ...
  	 */
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1038
1039
1040
  	err = create_ad1845(card, wss_port[dev], irq[dev],
  			    dma[dev], dma2[dev]);
  	if (err < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
1041
1042
1043
1044
  		snd_printk(KERN_ERR
  				"sscape: No AD1845 device at 0x%lx, IRQ %d
  ",
  				wss_port[dev], irq[dev]);
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1045
  		goto _release_dma;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1046
  	}
acd471009   Krzysztof Helt   ALSA: sscape: con...
1047
1048
1049
1050
1051
1052
1053
  	strcpy(card->driver, "SoundScape");
  	strcpy(card->shortname, name);
  	snprintf(card->longname, sizeof(card->longname),
  		 "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d
  ",
  		 name, sscape->chip->port, sscape->chip->irq,
  		 sscape->chip->dma1, sscape->chip->dma2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
  #define MIDI_DEVNUM  0
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
1055
  	if (sscape->type != SSCAPE_VIVO) {
acd471009   Krzysztof Helt   ALSA: sscape: con...
1056
1057
1058
  		err = sscape_upload_bootblock(card);
  		if (err >= 0)
  			err = sscape_upload_microcode(card, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059

acd471009   Krzysztof Helt   ALSA: sscape: con...
1060
1061
1062
1063
  		if (err == 0) {
  			err = create_mpu401(card, MIDI_DEVNUM, port[dev],
  					    mpu_irq[dev]);
  			if (err < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
1064
  				snd_printk(KERN_ERR "sscape: Failed to create "
acd471009   Krzysztof Helt   ALSA: sscape: con...
1065
1066
1067
1068
1069
1070
1071
  						"MPU-401 device at 0x%lx
  ",
  						port[dev]);
  				goto _release_dma;
  			}
  
  			/*
acd471009   Krzysztof Helt   ALSA: sscape: con...
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
  			 * Initialize mixer
  			 */
  			spin_lock_irqsave(&sscape->lock, flags);
  			sscape->midi_vol = 0;
  			host_write_ctrl_unsafe(sscape->io_base,
  						CMD_SET_MIDI_VOL, 100);
  			host_write_ctrl_unsafe(sscape->io_base,
  						sscape->midi_vol, 100);
  			host_write_ctrl_unsafe(sscape->io_base,
  						CMD_XXX_MIDI_VOL, 100);
  			host_write_ctrl_unsafe(sscape->io_base,
  						sscape->midi_vol, 100);
  			host_write_ctrl_unsafe(sscape->io_base,
  						CMD_SET_EXTMIDI, 100);
  			host_write_ctrl_unsafe(sscape->io_base,
  						0, 100);
  			host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
  
  			set_midi_mode_unsafe(sscape->io_base);
  			spin_unlock_irqrestore(&sscape->lock, flags);
  		}
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
1093
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1094
1095
1096
1097
1098
1099
1100
1101
  
  	/*
  	 * Now that we have successfully created this sound card,
  	 * it is safe to store the pointer.
  	 * NOTE: we only register the sound card's "destructor"
  	 *       function now that our "constructor" has completed.
  	 */
  	card->private_free = soundscape_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1102
1103
  
  	return 0;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1104
  _release_dma:
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1105
  	free_dma(dma[dev]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106

4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1107
  _release_region:
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
1108
  	release_and_free_resource(wss_res);
b1d5776d8   Takashi Iwai   [ALSA] Remove vma...
1109
  	release_and_free_resource(io_res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1110
1111
1112
  
  	return err;
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
  static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
  {
  	/*
  	 * Make sure we were given ALL of the other parameters.
  	 */
  	if (port[i] == SNDRV_AUTO_PORT)
  		return 0;
  
  	if (irq[i] == SNDRV_AUTO_IRQ ||
  	    mpu_irq[i] == SNDRV_AUTO_IRQ ||
  	    dma[i] == SNDRV_AUTO_DMA) {
  		printk(KERN_INFO
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
1125
1126
1127
  		       "sscape: insufficient parameters, "
  		       "need IO, IRQ, MPU-IRQ and DMA
  ");
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1128
1129
1130
1131
1132
1133
1134
  		return 0;
  	}
  
  	return 1;
  }
  
  static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1135
  {
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1136
  	struct snd_card *card;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1137
  	struct soundscape *sscape;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1138
  	int ret;
c95eadd2f   Takashi Iwai   ALSA: Convert to ...
1139
1140
1141
1142
  	ret = snd_card_create(index[dev], id[dev], THIS_MODULE,
  			      sizeof(struct soundscape), &card);
  	if (ret < 0)
  		return ret;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1143
1144
1145
  
  	sscape = get_card_soundscape(card);
  	sscape->type = SSCAPE;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1146
  	dma[dev] &= 0x03;
acd471009   Krzysztof Helt   ALSA: sscape: con...
1147
  	snd_card_set_dev(card, pdev);
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1148
  	ret = create_sscape(dev, card);
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1149
  	if (ret < 0)
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1150
  		goto _release_card;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
1151
1152
  	ret = snd_card_register(card);
  	if (ret < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
1153
1154
  		snd_printk(KERN_ERR "sscape: Failed to register sound card
  ");
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1155
  		goto _release_card;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1156
  	}
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1157
  	dev_set_drvdata(pdev, card);
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1158
  	return 0;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1159
1160
1161
1162
  
  _release_card:
  	snd_card_free(card);
  	return ret;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1163
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1164
  static int __devexit snd_sscape_remove(struct device *devptr, unsigned int dev)
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1165
  {
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1166
1167
  	snd_card_free(dev_get_drvdata(devptr));
  	dev_set_drvdata(devptr, NULL);
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1168
1169
  	return 0;
  }
83c51c0ab   Rene Herman   [ALSA] isa_bus de...
1170
  #define DEV_NAME "sscape"
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1171

5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1172
1173
  static struct isa_driver snd_sscape_driver = {
  	.match		= snd_sscape_match,
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1174
1175
1176
1177
  	.probe		= snd_sscape_probe,
  	.remove		= __devexit_p(snd_sscape_remove),
  	/* FIXME: suspend/resume */
  	.driver		= {
83c51c0ab   Rene Herman   [ALSA] isa_bus de...
1178
  		.name	= DEV_NAME
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1179
1180
  	},
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
1183
1184
  
  #ifdef CONFIG_PNP
  static inline int __devinit get_next_autoindex(int i)
  {
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1185
  	while (i < SNDRV_CARDS && port[i] != SNDRV_AUTO_PORT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1186
  		++i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1187
1188
  	return i;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1189
1190
1191
  static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
  				       const struct pnp_card_device_id *pid)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
  	static int idx = 0;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1193
1194
  	struct pnp_dev *dev;
  	struct snd_card *card;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1195
  	struct soundscape *sscape;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1196
1197
1198
1199
1200
1201
  	int ret;
  
  	/*
  	 * Allow this function to fail *quietly* if all the ISA PnP
  	 * devices were configured using module parameters instead.
  	 */
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
1202
1203
  	idx = get_next_autoindex(idx);
  	if (idx >= SNDRV_CARDS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1204
  		return -ENOSPC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1205
1206
  
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
1208
  	 * Check that we still have room for another sound card ...
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1209
  	dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
1210
  	if (!dev)
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1211
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212

277e926c9   Takashi Iwai   [ALSA] sscape - U...
1213
1214
  	if (!pnp_is_active(dev)) {
  		if (pnp_activate_dev(dev) < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
1215
1216
  			snd_printk(KERN_INFO "sscape: device is inactive
  ");
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1217
  			return -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1218
1219
  		}
  	}
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1220
  	/*
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1221
1222
1223
  	 * Create a new ALSA sound card entry, in anticipation
  	 * of detecting our hardware ...
  	 */
c95eadd2f   Takashi Iwai   ALSA: Convert to ...
1224
1225
1226
1227
  	ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
  			      sizeof(struct soundscape), &card);
  	if (ret < 0)
  		return ret;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
  
  	sscape = get_card_soundscape(card);
  
  	/*
  	 * Identify card model ...
  	 */
  	if (!strncmp("ENS4081", pid->id, 7))
  		sscape->type = SSCAPE_VIVO;
  	else
  		sscape->type = SSCAPE_PNP;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1238
  	/*
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1239
1240
1241
1242
1243
1244
  	 * Read the correct parameters off the ISA PnP bus ...
  	 */
  	port[idx] = pnp_port_start(dev, 0);
  	irq[idx] = pnp_irq(dev, 0);
  	mpu_irq[idx] = pnp_irq(dev, 1);
  	dma[idx] = pnp_dma(dev, 0) & 0x03;
ec1e79493   Krzysztof Helt   [ALSA] sscape: su...
1245
1246
1247
1248
1249
1250
1251
  	if (sscape->type == SSCAPE_PNP) {
  		dma2[idx] = dma[idx];
  		wss_port[idx] = CODEC_IO(port[idx]);
  	} else {
  		wss_port[idx] = pnp_port_start(dev, 1);
  		dma2[idx] = pnp_dma(dev, 1);
  	}
acd471009   Krzysztof Helt   ALSA: sscape: con...
1252
  	snd_card_set_dev(card, &pcard->card->dev);
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1253

4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1254
  	ret = create_sscape(idx, card);
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1255
  	if (ret < 0)
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1256
  		goto _release_card;
6fcfa3959   Krzysztof Helt   ALSA: sscape: cod...
1257
1258
  	ret = snd_card_register(card);
  	if (ret < 0) {
bcde1f8a8   Krzysztof Helt   ALSA: sscape: rem...
1259
1260
  		snd_printk(KERN_ERR "sscape: Failed to register sound card
  ");
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1261
  		goto _release_card;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1262
1263
1264
1265
  	}
  
  	pnp_set_card_drvdata(pcard, card);
  	++idx;
4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1266
  	return 0;
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1267

4996bca98   Krzysztof Helt   [ALSA] sscape: dr...
1268
1269
  _release_card:
  	snd_card_free(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1270
1271
1272
1273
1274
  	return ret;
  }
  
  static void __devexit sscape_pnp_remove(struct pnp_card_link * pcard)
  {
277e926c9   Takashi Iwai   [ALSA] sscape - U...
1275
  	snd_card_free(pnp_get_card_drvdata(pcard));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1276
  	pnp_set_card_drvdata(pcard, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
  }
  
  static struct pnp_card_driver sscape_pnpc_driver = {
  	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
  	.name = "sscape",
  	.id_table = sscape_pnpids,
  	.probe = sscape_pnp_detect,
  	.remove = __devexit_p(sscape_pnp_remove),
  };
  
  #endif /* CONFIG_PNP */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288
1289
  static int __init sscape_init(void)
  {
609d76941   Rene Herman   [ALSA] Fix probe ...
1290
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1291

609d76941   Rene Herman   [ALSA] Fix probe ...
1292
  	err = isa_register_driver(&snd_sscape_driver, SNDRV_CARDS);
59b1b34f4   Takashi Iwai   [ALSA] Fix compil...
1293
  #ifdef CONFIG_PNP
609d76941   Rene Herman   [ALSA] Fix probe ...
1294
1295
1296
1297
1298
  	if (!err)
  		isa_registered = 1;
  
  	err = pnp_register_card_driver(&sscape_pnpc_driver);
  	if (!err)
f7a9275d9   Clemens Ladisch   [ALSA] unregister...
1299
  		pnp_registered = 1;
609d76941   Rene Herman   [ALSA] Fix probe ...
1300
1301
1302
  
  	if (isa_registered)
  		err = 0;
59b1b34f4   Takashi Iwai   [ALSA] Fix compil...
1303
  #endif
609d76941   Rene Herman   [ALSA] Fix probe ...
1304
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1305
  }
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1306
1307
1308
1309
1310
  static void __exit sscape_exit(void)
  {
  #ifdef CONFIG_PNP
  	if (pnp_registered)
  		pnp_unregister_card_driver(&sscape_pnpc_driver);
609d76941   Rene Herman   [ALSA] Fix probe ...
1311
  	if (isa_registered)
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1312
  #endif
609d76941   Rene Herman   [ALSA] Fix probe ...
1313
  		isa_unregister_driver(&snd_sscape_driver);
5e24c1c1c   Takashi Iwai   [ALSA] Port the r...
1314
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1315
1316
  module_init(sscape_init);
  module_exit(sscape_exit);