source: GPL/alsa-kernel/isa/sb/emu8000.c@ 1

Last change on this file since 1 was 1, checked in by vladest, 20 years ago

initial import

File size: 37.6 KB
Line 
1/*
2 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3 * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
4 * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
5 *
6 * Routines for control of EMU8000 chip
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., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#define SNDRV_MAIN_OBJECT_FILE
24#include <sound/emu8000.h>
25#include <sound/emu8000_reg.h>
26#include <sound/control.h>
27#include <sound/initval.h>
28
29MODULE_CLASSES("{sound}");
30MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
31
32/*
33 * emu8000 register controls
34 */
35
36/*
37 * The following routines read and write registers on the emu8000. They
38 * should always be called via the EMU8000*READ/WRITE macros and never
39 * directly. The macros handle the port number and command word.
40 */
41/* Write a word */
42void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
43{
44 unsigned long flags;
45 spin_lock_irqsave(&emu->reg_lock, flags);
46 if (reg != emu->last_reg) {
47 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
48 emu->last_reg = reg;
49 }
50 outw((unsigned short)val, port); /* Send data */
51 spin_unlock_irqrestore(&emu->reg_lock, flags);
52}
53
54/* Read a word */
55unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg)
56{
57 unsigned short res;
58 unsigned long flags;
59 spin_lock_irqsave(&emu->reg_lock, flags);
60 if (reg != emu->last_reg) {
61 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
62 emu->last_reg = reg;
63 }
64 res = inw(port); /* Read data */
65 spin_unlock_irqrestore(&emu->reg_lock, flags);
66 return res;
67}
68
69/* Write a double word */
70void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
71{
72 unsigned long flags;
73 spin_lock_irqsave(&emu->reg_lock, flags);
74 if (reg != emu->last_reg) {
75 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
76 emu->last_reg = reg;
77 }
78 outw((unsigned short)val, port); /* Send low word of data */
79 outw((unsigned short)(val>>16), port+2); /* Send high word of data */
80 spin_unlock_irqrestore(&emu->reg_lock, flags);
81}
82
83/* Read a double word */
84unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg)
85{
86 unsigned short low;
87 unsigned int res;
88 unsigned long flags;
89 spin_lock_irqsave(&emu->reg_lock, flags);
90 if (reg != emu->last_reg) {
91 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
92 emu->last_reg = reg;
93 }
94 low = inw(port); /* Read low word of data */
95 res = low + (inw(port+2) << 16);
96 spin_unlock_irqrestore(&emu->reg_lock, flags);
97 return res;
98}
99
100/*
101 * Set up / close a channel to be used for DMA.
102 */
103/*exported*/ void
104snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode)
105{
106 if (mode == EMU8000_RAM_CLOSE) {
107 EMU8000_CCCA_WRITE(emu, ch, 0);
108 EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F);
109 return;
110 }
111 EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
112 EMU8000_VTFT_WRITE(emu, ch, 0);
113 EMU8000_CVCF_WRITE(emu, ch, 0);
114 EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
115 EMU8000_CPF_WRITE(emu, ch, 0x40000000);
116 EMU8000_PSST_WRITE(emu, ch, 0);
117 EMU8000_CSL_WRITE(emu, ch, 0);
118 if (mode == EMU8000_RAM_WRITE) /* DMA write */
119 EMU8000_CCCA_WRITE(emu, ch, 0x06000000);
120 else /* DMA read */
121 EMU8000_CCCA_WRITE(emu, ch, 0x04000000);
122}
123
124/*
125 */
126static void /*__init*/
127snd_emu8000_read_wait(emu8000_t *emu)
128{
129 while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
130 set_current_state(TASK_INTERRUPTIBLE);
131 schedule_timeout(1);
132 if (signal_pending(current))
133 break;
134 }
135}
136
137/*
138 */
139static void /*__init*/
140snd_emu8000_write_wait(emu8000_t *emu)
141{
142 while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
143 set_current_state(TASK_INTERRUPTIBLE);
144 schedule_timeout(1);
145 if (signal_pending(current))
146 break;
147 }
148}
149
150/*
151 * detect a card at the given port
152 */
153static int /*__init*/
154snd_emu8000_detect(emu8000_t *emu)
155{
156 /* Initialise */
157 EMU8000_HWCF1_WRITE(emu, 0x0059);
158 EMU8000_HWCF2_WRITE(emu, 0x0020);
159 EMU8000_HWCF3_WRITE(emu, 0x0000);
160 /* Check for a recognisable emu8000 */
161 /*
162 if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c)
163 return -ENODEV;
164 */
165 if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058)
166 return -ENODEV;
167 if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
168 return -ENODEV;
169
170 snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
171 emu->port1);
172 return 0;
173}
174
175
176/*
177 * intiailize audio channels
178 */
179static void /*__init*/
180init_audio(emu8000_t *emu)
181{
182 int ch;
183
184 /* turn off envelope engines */
185 for (ch = 0; ch < EMU8000_CHANNELS; ch++)
186 EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
187
188 /* reset all other parameters to zero */
189 for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
190 EMU8000_ENVVOL_WRITE(emu, ch, 0);
191 EMU8000_ENVVAL_WRITE(emu, ch, 0);
192 EMU8000_DCYSUS_WRITE(emu, ch, 0);
193 EMU8000_ATKHLDV_WRITE(emu, ch, 0);
194 EMU8000_LFO1VAL_WRITE(emu, ch, 0);
195 EMU8000_ATKHLD_WRITE(emu, ch, 0);
196 EMU8000_LFO2VAL_WRITE(emu, ch, 0);
197 EMU8000_IP_WRITE(emu, ch, 0);
198 EMU8000_IFATN_WRITE(emu, ch, 0);
199 EMU8000_PEFE_WRITE(emu, ch, 0);
200 EMU8000_FMMOD_WRITE(emu, ch, 0);
201 EMU8000_TREMFRQ_WRITE(emu, ch, 0);
202 EMU8000_FM2FRQ2_WRITE(emu, ch, 0);
203 EMU8000_PTRX_WRITE(emu, ch, 0);
204 EMU8000_VTFT_WRITE(emu, ch, 0);
205 EMU8000_PSST_WRITE(emu, ch, 0);
206 EMU8000_CSL_WRITE(emu, ch, 0);
207 EMU8000_CCCA_WRITE(emu, ch, 0);
208 }
209
210 for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
211 EMU8000_CPF_WRITE(emu, ch, 0);
212 EMU8000_CVCF_WRITE(emu, ch, 0);
213 }
214}
215
216
217/*
218 * initialize DMA address
219 */
220static void /*__init*/
221init_dma(emu8000_t *emu)
222{
223 EMU8000_SMALR_WRITE(emu, 0);
224 EMU8000_SMARR_WRITE(emu, 0);
225 EMU8000_SMALW_WRITE(emu, 0);
226 EMU8000_SMARW_WRITE(emu, 0);
227}
228
229/*
230 * initialization arrays; from ADIP
231 */
232static unsigned short init1[128] /*__devinitdata*/ = {
233 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
234 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
235 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
236 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30,
237
238 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330,
239 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730,
240 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30,
241 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30,
242
243 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330,
244 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730,
245 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30,
246 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30,
247
248 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330,
249 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730,
250 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30,
251 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
252};
253
254static unsigned short init2[128] /*__devinitdata*/ = {
255 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
256 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
257 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
258 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
259
260 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
261 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
262 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
263 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
264
265 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
266 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
267 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
268 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
269
270 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
271 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
272 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
273 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
274};
275
276static unsigned short init3[128] /*__devinitdata*/ = {
277 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
278 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
279 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
280 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
281
282 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
283 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
284 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
285 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
286
287 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
288 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
289 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
290 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
291
292 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
293 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
294 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
295 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
296};
297
298static unsigned short init4[128] /*__devinitdata*/ = {
299 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
300 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
301 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
302 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
303
304 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
305 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
306 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
307 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
308
309 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
310 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
311 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
312 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
313
314 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
315 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
316 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
317 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
318};
319
320/* send an initialization array
321 * Taken from the oss driver, not obvious from the doc how this
322 * is meant to work
323 */
324static void /*__init*/
325send_array(emu8000_t *emu, unsigned short *data, int size)
326{
327 int i;
328 unsigned short *p;
329
330 p = data;
331 for (i = 0; i < size; i++, p++)
332 EMU8000_INIT1_WRITE(emu, i, *p);
333 for (i = 0; i < size; i++, p++)
334 EMU8000_INIT2_WRITE(emu, i, *p);
335 for (i = 0; i < size; i++, p++)
336 EMU8000_INIT3_WRITE(emu, i, *p);
337 for (i = 0; i < size; i++, p++)
338 EMU8000_INIT4_WRITE(emu, i, *p);
339}
340
341#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0]))
342
343
344/*
345 * Send initialization arrays to start up, this just follows the
346 * initialisation sequence in the adip.
347 */
348static void /*__init*/
349init_arrays(emu8000_t *emu)
350{
351 send_array(emu, init1, NELEM(init1)/4);
352
353 set_current_state(TASK_INTERRUPTIBLE);
354 schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */
355 send_array(emu, init2, NELEM(init2)/4);
356 send_array(emu, init3, NELEM(init3)/4);
357
358 EMU8000_HWCF4_WRITE(emu, 0);
359 EMU8000_HWCF5_WRITE(emu, 0x83);
360 EMU8000_HWCF6_WRITE(emu, 0x8000);
361
362 send_array(emu, init4, NELEM(init4)/4);
363}
364
365
366#define UNIQUE_ID1 0xa5b9
367#define UNIQUE_ID2 0x9d53
368
369/*
370 * Size the onboard memory.
371 * This is written so as not to need arbitary delays after the write. It
372 * seems that the only way to do this is to use the one channel and keep
373 * reallocating between read and write.
374 */
375static void /*__init*/
376size_dram(emu8000_t *emu)
377{
378 int i, size;
379
380 if (emu->dram_checked)
381 return;
382
383 size = 0;
384
385 /* write out a magic number */
386 snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
387 snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
388 EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
389 EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
390 snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
391
392 while (size < EMU8000_MAX_DRAM) {
393
394 size += 512 * 1024; /* increment 512kbytes */
395
396 /* Write a unique data on the test address.
397 * if the address is out of range, the data is written on
398 * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is
399 * changed by this data.
400 */
401 /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
402 EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
403 EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
404 snd_emu8000_write_wait(emu);
405
406 /*
407 * read the data on the just written DRAM address
408 * if not the same then we have reached the end of ram.
409 */
410 /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
411 EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
412 /*snd_emu8000_read_wait(emu);*/
413 EMU8000_SMLD_READ(emu); /* discard stale data */
414 if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
415 break; /* we must have wrapped around */
416
417 snd_emu8000_read_wait(emu);
418
419 /*
420 * If it is the same it could be that the address just
421 * wraps back to the beginning; so check to see if the
422 * initial value has been overwritten.
423 */
424 EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
425 EMU8000_SMLD_READ(emu); /* discard stale data */
426 if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
427 break; /* we must have wrapped around */
428 snd_emu8000_read_wait(emu);
429 }
430
431 /* wait until FULL bit in SMAxW register is false */
432 for (i = 0; i < 10000; i++) {
433 if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
434 break;
435 set_current_state(TASK_INTERRUPTIBLE);
436 schedule_timeout(1);
437 if (signal_pending(current))
438 break;
439 }
440 snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
441 snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
442
443 snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
444 emu->port1, size/1024);
445
446 emu->mem_size = size;
447 emu->dram_checked = 1;
448}
449
450
451/*
452 * Initiailise the FM section. You have to do this to use sample RAM
453 * and therefore lose 2 voices.
454 */
455/*exported*/ void
456snd_emu8000_init_fm(emu8000_t *emu)
457{
458 unsigned long flags;
459
460 /* Initialize the last two channels for DRAM refresh and producing
461 the reverb and chorus effects for Yamaha OPL-3 synthesizer */
462
463 /* 31: FM left channel, 0xffffe0-0xffffe8 */
464 EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
465 EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
466 EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
467 EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
468 EMU8000_CPF_WRITE(emu, 30, 0);
469 EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
470
471 /* 32: FM right channel, 0xfffff0-0xfffff8 */
472 EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
473 EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
474 EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
475 EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
476 EMU8000_CPF_WRITE(emu, 31, 0x8000);
477 EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
478
479 snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
480
481 spin_lock_irqsave(&emu->reg_lock, flags);
482 while (!(inw(EMU8000_PTR(emu)) & 0x1000))
483 ;
484 while ((inw(EMU8000_PTR(emu)) & 0x1000))
485 ;
486 spin_unlock_irqrestore(&emu->reg_lock, flags);
487 snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
488 /* this is really odd part.. */
489 outb(0x3C, EMU8000_PTR(emu));
490 outb(0, EMU8000_DATA1(emu));
491
492 /* skew volume & cutoff */
493 EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF);
494 EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF);
495}
496
497
498/*
499 * The main initialization routine.
500 */
501static void /*__init*/
502snd_emu8000_init_hw(emu8000_t *emu)
503{
504 int i;
505
506 emu->last_reg = 0xffff; /* reset the last register index */
507
508 /* initialize hardware configuration */
509 EMU8000_HWCF1_WRITE(emu, 0x0059);
510 EMU8000_HWCF2_WRITE(emu, 0x0020);
511
512 /* disable audio; this seems to reduce a clicking noise a bit.. */
513 EMU8000_HWCF3_WRITE(emu, 0);
514
515 /* initialize audio channels */
516 init_audio(emu);
517
518 /* initialize DMA */
519 init_dma(emu);
520
521 /* initialize init arrays */
522 init_arrays(emu);
523
524 /*
525 * Initialize the FM section of the AWE32, this is needed
526 * for DRAM refresh as well
527 */
528 snd_emu8000_init_fm(emu);
529
530 /* terminate all voices */
531 for (i = 0; i < EMU8000_DRAM_VOICES; i++)
532 EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F);
533
534 /* check DRAM memory size */
535 size_dram(emu);
536
537 /* enable audio */
538 EMU8000_HWCF3_WRITE(emu, 0x4);
539
540 /* set equzlier, chorus and reverb modes */
541 snd_emu8000_update_equalizer(emu);
542 snd_emu8000_update_chorus_mode(emu);
543 snd_emu8000_update_reverb_mode(emu);
544}
545
546
547/*----------------------------------------------------------------
548 * Bass/Treble Equalizer
549 *----------------------------------------------------------------*/
550
551static unsigned short bass_parm[12][3] = {
552 {0xD26A, 0xD36A, 0x0000}, /* -12 dB */
553 {0xD25B, 0xD35B, 0x0000}, /* -8 */
554 {0xD24C, 0xD34C, 0x0000}, /* -6 */
555 {0xD23D, 0xD33D, 0x0000}, /* -4 */
556 {0xD21F, 0xD31F, 0x0000}, /* -2 */
557 {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */
558 {0xC219, 0xC319, 0x0001}, /* +2 */
559 {0xC22A, 0xC32A, 0x0001}, /* +4 */
560 {0xC24C, 0xC34C, 0x0001}, /* +6 */
561 {0xC26E, 0xC36E, 0x0001}, /* +8 */
562 {0xC248, 0xC384, 0x0002}, /* +10 */
563 {0xC26A, 0xC36A, 0x0002}, /* +12 dB */
564};
565
566static unsigned short treble_parm[12][9] = {
567 {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
568 {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
569 {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
570 {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
571 {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
572 {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
573 {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
574 {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
575 {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
576 {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
577 {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
578 {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */
579};
580
581
582/*
583 * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
584 */
585/*exported*/ void
586snd_emu8000_update_equalizer(emu8000_t *emu)
587{
588 unsigned short w;
589 int bass = emu->bass_level;
590 int treble = emu->treble_level;
591
592 if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
593 return;
594 EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]);
595 EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]);
596 EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]);
597 EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]);
598 EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]);
599 EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]);
600 EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]);
601 EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]);
602 EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]);
603 EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]);
604 w = bass_parm[bass][2] + treble_parm[treble][8];
605 EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262));
606 EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362));
607}
608
609
610/*----------------------------------------------------------------
611 * Chorus mode control
612 *----------------------------------------------------------------*/
613
614/*
615 * chorus mode parameters
616 */
617#define SNDRV_EMU8000_CHORUS_1 0
618#define SNDRV_EMU8000_CHORUS_2 1
619#define SNDRV_EMU8000_CHORUS_3 2
620#define SNDRV_EMU8000_CHORUS_4 3
621#define SNDRV_EMU8000_CHORUS_FEEDBACK 4
622#define SNDRV_EMU8000_CHORUS_FLANGER 5
623#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6
624#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7
625#define SNDRV_EMU8000_CHORUS_PREDEFINED 8
626/* user can define chorus modes up to 32 */
627#define SNDRV_EMU8000_CHORUS_NUMBERS 32
628
629typedef struct soundfont_chorus_fx_t {
630 unsigned short feedback; /* feedback level (0xE600-0xE6FF) */
631 unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */
632 unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */
633 unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
634 unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */
635} soundfont_chorus_fx_t;
636
637/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
638static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
639static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
640 {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
641 {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
642 {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
643 {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
644 {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
645 {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
646 {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */
647 {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */
648};
649
650/*exported*/ int
651snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len)
652{
653 soundfont_chorus_fx_t rec;
654 if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
655 snd_printk("illegal chorus mode %d for uploading\n", mode);
656 return -EINVAL;
657 }
658 if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
659 return -EFAULT;
660 chorus_parm[mode] = rec;
661 chorus_defined[mode] = 1;
662 return 0;
663}
664
665/*exported*/ void
666snd_emu8000_update_chorus_mode(emu8000_t *emu)
667{
668 int effect = emu->chorus_mode;
669 if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
670 (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
671 return;
672 EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback);
673 EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset);
674 EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth);
675 EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
676 EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
677 EMU8000_HWCF6_WRITE(emu, 0x8000);
678 EMU8000_HWCF7_WRITE(emu, 0x0000);
679}
680
681/*----------------------------------------------------------------
682 * Reverb mode control
683 *----------------------------------------------------------------*/
684
685/*
686 * reverb mode parameters
687 */
688#define SNDRV_EMU8000_REVERB_ROOM1 0
689#define SNDRV_EMU8000_REVERB_ROOM2 1
690#define SNDRV_EMU8000_REVERB_ROOM3 2
691#define SNDRV_EMU8000_REVERB_HALL1 3
692#define SNDRV_EMU8000_REVERB_HALL2 4
693#define SNDRV_EMU8000_REVERB_PLATE 5
694#define SNDRV_EMU8000_REVERB_DELAY 6
695#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7
696#define SNDRV_EMU8000_REVERB_PREDEFINED 8
697/* user can define reverb modes up to 32 */
698#define SNDRV_EMU8000_REVERB_NUMBERS 32
699
700typedef struct soundfont_reverb_fx_t {
701 unsigned short parms[28];
702} soundfont_reverb_fx_t;
703
704/* reverb mode settings; write the following 28 data of 16 bit length
705 * on the corresponding ports in the reverb_cmds array
706 */
707static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
708static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
709{{ /* room 1 */
710 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
711 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
712 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
713 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
714}},
715{{ /* room 2 */
716 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
717 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
718 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
719 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
720}},
721{{ /* room 3 */
722 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
723 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
724 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
725 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
726}},
727{{ /* hall 1 */
728 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
729 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
730 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
731 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
732}},
733{{ /* hall 2 */
734 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
735 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
736 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
737 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
738}},
739{{ /* plate */
740 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
741 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
742 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
743 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
744}},
745{{ /* delay */
746 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
747 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
748 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
749 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
750}},
751{{ /* panning delay */
752 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
753 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
754 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
755 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
756}},
757};
758
759enum { DATA1, DATA2 };
760#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1
761#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2
762#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1
763#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2
764
765static struct reverb_cmd_pair {
766 unsigned short cmd, port;
767} reverb_cmds[28] = {
768 {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
769 {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
770 {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
771 {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
772 {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
773 {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
774 {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
775};
776
777/*exported*/ int
778snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len)
779{
780 soundfont_reverb_fx_t rec;
781
782 if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
783 snd_printk("illegal reverb mode %d for uploading\n", mode);
784 return -EINVAL;
785 }
786 if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
787 return -EFAULT;
788 reverb_parm[mode] = rec;
789 reverb_defined[mode] = 1;
790 return 0;
791}
792
793/*exported*/ void
794snd_emu8000_update_reverb_mode(emu8000_t *emu)
795{
796 int effect = emu->reverb_mode;
797 int i;
798
799 if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
800 (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
801 return;
802 for (i = 0; i < 28; i++) {
803 int port;
804 if (reverb_cmds[i].port == DATA1)
805 port = EMU8000_DATA1(emu);
806 else
807 port = EMU8000_DATA2(emu);
808 snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
809 }
810}
811
812
813/*----------------------------------------------------------------
814 * mixer interface
815 *----------------------------------------------------------------*/
816
817#define chip_t emu8000_t
818
819/*
820 * bass/treble
821 */
822static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
823{
824 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
825 uinfo->count = 1;
826 uinfo->value.integer.min = 0;
827 uinfo->value.integer.max = 11;
828 return 0;
829}
830
831static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
832{
833 emu8000_t *emu = snd_kcontrol_chip(kcontrol);
834
835 ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
836 return 0;
837}
838
839static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
840{
841 emu8000_t *emu = snd_kcontrol_chip(kcontrol);
842 unsigned long flags;
843 int change;
844 unsigned short val1;
845
846 val1 = ucontrol->value.integer.value[0] % 12;
847 spin_lock_irqsave(&emu->control_lock, flags);
848 if (kcontrol->private_value) {
849 change = val1 != emu->treble_level;
850 emu->treble_level = val1;
851 } else {
852 change = val1 != emu->bass_level;
853 emu->bass_level = val1;
854 }
855 spin_unlock_irqrestore(&emu->control_lock, flags);
856 snd_emu8000_update_equalizer(emu);
857 return change;
858}
859
860#ifdef TARGET_OS2
861static snd_kcontrol_new_t mixer_bass_control =
862{
863 SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
864 "Synth Tone Control - Bass",0,0, 0,
865 mixer_bass_treble_info,
866 mixer_bass_treble_get,
867 mixer_bass_treble_put,
868 0,
869};
870
871static snd_kcontrol_new_t mixer_treble_control =
872{
873 SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
874 "Synth Tone Control - Treble",0,0, 0,
875 mixer_bass_treble_info,
876 mixer_bass_treble_get,
877 mixer_bass_treble_put,
878 1,
879};
880#else
881static snd_kcontrol_new_t mixer_bass_control =
882{
883 iface: SNDRV_CTL_ELEM_IFACE_MIXER,
884 name: "Synth Tone Control - Bass",
885 info: mixer_bass_treble_info,
886 get: mixer_bass_treble_get,
887 put: mixer_bass_treble_put,
888 private_value: 0,
889};
890
891static snd_kcontrol_new_t mixer_treble_control =
892{
893 iface: SNDRV_CTL_ELEM_IFACE_MIXER,
894 name: "Synth Tone Control - Treble",
895 info: mixer_bass_treble_info,
896 get: mixer_bass_treble_get,
897 put: mixer_bass_treble_put,
898 private_value: 1,
899};
900#endif
901
902/*
903 * chorus/reverb mode
904 */
905static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
906{
907 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
908 uinfo->count = 1;
909 uinfo->value.integer.min = 0;
910 uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1);
911 return 0;
912}
913
914static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
915{
916 emu8000_t *emu = snd_kcontrol_chip(kcontrol);
917
918 ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
919 return 0;
920}
921
922static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
923{
924 emu8000_t *emu = snd_kcontrol_chip(kcontrol);
925 unsigned long flags;
926 int change;
927 unsigned short val1;
928
929 spin_lock_irqsave(&emu->control_lock, flags);
930 if (kcontrol->private_value) {
931 val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
932 change = val1 != emu->chorus_mode;
933 emu->chorus_mode = val1;
934 } else {
935 val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
936 change = val1 != emu->reverb_mode;
937 emu->reverb_mode = val1;
938 }
939 spin_unlock_irqrestore(&emu->control_lock, flags);
940 if (change) {
941 if (kcontrol->private_value)
942 snd_emu8000_update_chorus_mode(emu);
943 else
944 snd_emu8000_update_reverb_mode(emu);
945 }
946 return change;
947}
948
949#ifdef TARGET_OS2
950static snd_kcontrol_new_t mixer_chorus_mode_control =
951{
952 SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
953 "Chorus Mode",0,0, 0,
954 mixer_chorus_reverb_info,
955 mixer_chorus_reverb_get,
956 mixer_chorus_reverb_put,
957 1,
958};
959
960static snd_kcontrol_new_t mixer_reverb_mode_control =
961{
962 SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
963 "Reverb Mode",0,0,0,
964 mixer_chorus_reverb_info,
965 mixer_chorus_reverb_get,
966 mixer_chorus_reverb_put,
967 0,
968};
969#else
970static snd_kcontrol_new_t mixer_chorus_mode_control =
971{
972 iface: SNDRV_CTL_ELEM_IFACE_MIXER,
973 name: "Chorus Mode",
974 info: mixer_chorus_reverb_info,
975 get: mixer_chorus_reverb_get,
976 put: mixer_chorus_reverb_put,
977 private_value: 1,
978};
979
980static snd_kcontrol_new_t mixer_reverb_mode_control =
981{
982 iface: SNDRV_CTL_ELEM_IFACE_MIXER,
983 name: "Reverb Mode",
984 info: mixer_chorus_reverb_info,
985 get: mixer_chorus_reverb_get,
986 put: mixer_chorus_reverb_put,
987 private_value: 0,
988};
989#endif
990
991/*
992 * FM OPL3 chorus/reverb depth
993 */
994static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
995{
996 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
997 uinfo->count = 1;
998 uinfo->value.integer.min = 0;
999 uinfo->value.integer.max = 255;
1000 return 0;
1001}
1002
1003static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
1004{
1005 emu8000_t *emu = snd_kcontrol_chip(kcontrol);
1006
1007 ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
1008 return 0;
1009}
1010
1011static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
1012{
1013 emu8000_t *emu = snd_kcontrol_chip(kcontrol);
1014 unsigned long flags;
1015 int change;
1016 unsigned short val1;
1017
1018 val1 = ucontrol->value.integer.value[0] % 256;
1019 spin_lock_irqsave(&emu->control_lock, flags);
1020 if (kcontrol->private_value) {
1021 change = val1 != emu->fm_chorus_depth;
1022 emu->fm_chorus_depth = val1;
1023 } else {
1024 change = val1 != emu->fm_reverb_depth;
1025 emu->fm_reverb_depth = val1;
1026 }
1027 spin_unlock_irqrestore(&emu->control_lock, flags);
1028 if (change)
1029 snd_emu8000_init_fm(emu);
1030 return change;
1031}
1032
1033#ifdef TARGET_OS2
1034static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
1035{
1036 SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
1037 "FM Chorus Depth",0,0,0,
1038 mixer_fm_depth_info,
1039 mixer_fm_depth_get,
1040 mixer_fm_depth_put,
1041 1,
1042};
1043
1044static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
1045{
1046 SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
1047 "FM Reverb Depth",0,0,0,
1048 mixer_fm_depth_info,
1049 mixer_fm_depth_get,
1050 mixer_fm_depth_put,
1051 0,
1052};
1053#else
1054static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
1055{
1056 iface: SNDRV_CTL_ELEM_IFACE_MIXER,
1057 name: "FM Chorus Depth",
1058 info: mixer_fm_depth_info,
1059 get: mixer_fm_depth_get,
1060 put: mixer_fm_depth_put,
1061 private_value: 1,
1062};
1063
1064static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
1065{
1066 iface: SNDRV_CTL_ELEM_IFACE_MIXER,
1067 name: "FM Reverb Depth",
1068 info: mixer_fm_depth_info,
1069 get: mixer_fm_depth_get,
1070 put: mixer_fm_depth_put,
1071 private_value: 0,
1072};
1073#endif
1074
1075static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = {
1076 &mixer_bass_control,
1077 &mixer_treble_control,
1078 &mixer_chorus_mode_control,
1079 &mixer_reverb_mode_control,
1080 &mixer_fm_chorus_depth_control,
1081 &mixer_fm_reverb_depth_control,
1082};
1083
1084/*
1085 * create and attach mixer elements for WaveTable treble/bass controls
1086 */
1087static int /*__init*/
1088snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu)
1089{
1090 int i, err = 0;
1091
1092 snd_assert(emu != NULL && card != NULL, return -EINVAL);
1093
1094 spin_lock_init(&emu->control_lock);
1095
1096 memset(emu->controls, 0, sizeof(emu->controls));
1097 for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
1098 if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
1099 goto __error;
1100 }
1101 return 0;
1102
1103__error:
1104 for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
1105 if (emu->controls[i])
1106 snd_ctl_remove(card, emu->controls[i]);
1107 }
1108 return err;
1109}
1110
1111
1112/*
1113 * free resources
1114 */
1115static int snd_emu8000_free(emu8000_t *hw)
1116{
1117 if (hw->res_port1)
1118 release_resource(hw->res_port1);
1119 if (hw->res_port2)
1120 release_resource(hw->res_port2);
1121 if (hw->res_port3)
1122 release_resource(hw->res_port3);
1123 snd_magic_kfree(hw);
1124 return 0;
1125}
1126
1127/*
1128 */
1129static int snd_emu8000_dev_free(snd_device_t *device)
1130{
1131 emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO);
1132 return snd_emu8000_free(hw);
1133}
1134
1135/*
1136 * initialize and register emu8000 synth device.
1137 */
1138/*exported*/ int
1139snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret)
1140{
1141 snd_seq_device_t *awe;
1142 emu8000_t *hw;
1143 int err;
1144#ifdef TARGET_OS2
1145 static snd_device_ops_t ops = {
1146 snd_emu8000_dev_free,0,0
1147 };
1148#else
1149 static snd_device_ops_t ops = {
1150 dev_free: snd_emu8000_dev_free,
1151 };
1152#endif
1153
1154 if (awe_ret)
1155 *awe_ret = NULL;
1156
1157 if (seq_ports <= 0)
1158 return 0;
1159
1160 hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL);
1161 if (hw == NULL)
1162 return -ENOMEM;
1163 spin_lock_init(&hw->reg_lock);
1164 hw->index = index;
1165 hw->port1 = port;
1166 hw->port2 = port + 0x400;
1167 hw->port3 = port + 0x800;
1168 if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
1169 !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
1170 !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
1171 snd_emu8000_free(hw);
1172 return -EBUSY;
1173 }
1174 hw->mem_size = 0;
1175 hw->card = card;
1176 hw->seq_ports = seq_ports;
1177 hw->bass_level = 5;
1178 hw->treble_level = 9;
1179 hw->chorus_mode = 2;
1180 hw->reverb_mode = 4;
1181 hw->fm_chorus_depth = 0;
1182 hw->fm_reverb_depth = 0;
1183
1184 if (snd_emu8000_detect(hw) < 0) {
1185 snd_emu8000_free(hw);
1186 return -ENODEV;
1187 }
1188
1189 snd_emu8000_init_hw(hw);
1190 if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
1191 snd_emu8000_free(hw);
1192 return err;
1193 }
1194
1195 if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hw, &ops)) < 0) {
1196 snd_emu8000_free(hw);
1197 return err;
1198 }
1199 if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
1200 sizeof(emu8000_t*), &awe) >= 0) {
1201 strcpy(awe->name, "EMU-8000");
1202 *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
1203 }
1204 if (awe_ret)
1205 *awe_ret = awe;
1206
1207 return 0;
1208}
1209
1210
1211/*
1212 * exported stuff
1213 */
1214
1215EXPORT_SYMBOL(snd_emu8000_new);
1216EXPORT_SYMBOL(snd_emu8000_poke);
1217EXPORT_SYMBOL(snd_emu8000_peek);
1218EXPORT_SYMBOL(snd_emu8000_poke_dw);
1219EXPORT_SYMBOL(snd_emu8000_peek_dw);
1220EXPORT_SYMBOL(snd_emu8000_dma_chan);
1221EXPORT_SYMBOL(snd_emu8000_init_fm);
1222EXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
1223EXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
1224EXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
1225EXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
1226EXPORT_SYMBOL(snd_emu8000_update_equalizer);
1227
1228/*
1229 * INIT part
1230 */
1231
1232static int __init alsa_emu8000_init(void)
1233{
1234 return 0;
1235}
1236
1237static void __exit alsa_emu8000_exit(void)
1238{
1239}
1240
1241module_init(alsa_emu8000_init)
1242module_exit(alsa_emu8000_exit)
Note: See TracBrowser for help on using the repository browser.