blob: 71887874679c1ae7d93b436a7998e86c5717e7ba [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Jaroslav Kyselac1017a42007-10-15 09:50:19 +02002 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/wait.h>
24#include <linux/sched.h>
25#include <linux/slab.h>
26#include <linux/ioport.h>
Paul Gortmakerd81a6d72011-09-22 09:34:58 -040027#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/delay.h>
29#include <sound/core.h>
30#include <sound/emu8000.h>
31#include <sound/emu8000_reg.h>
32#include <asm/io.h>
33#include <asm/uaccess.h>
34#include <linux/init.h>
35#include <sound/control.h>
36#include <sound/initval.h>
37
38/*
39 * emu8000 register controls
40 */
41
42/*
43 * The following routines read and write registers on the emu8000. They
44 * should always be called via the EMU8000*READ/WRITE macros and never
45 * directly. The macros handle the port number and command word.
46 */
47/* Write a word */
Takashi Iwai029d64b2005-11-17 14:34:36 +010048void snd_emu8000_poke(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049{
50 unsigned long flags;
51 spin_lock_irqsave(&emu->reg_lock, flags);
52 if (reg != emu->last_reg) {
53 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
54 emu->last_reg = reg;
55 }
56 outw((unsigned short)val, port); /* Send data */
57 spin_unlock_irqrestore(&emu->reg_lock, flags);
58}
59
60/* Read a word */
Takashi Iwai029d64b2005-11-17 14:34:36 +010061unsigned short snd_emu8000_peek(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062{
63 unsigned short res;
64 unsigned long flags;
65 spin_lock_irqsave(&emu->reg_lock, flags);
66 if (reg != emu->last_reg) {
67 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
68 emu->last_reg = reg;
69 }
70 res = inw(port); /* Read data */
71 spin_unlock_irqrestore(&emu->reg_lock, flags);
72 return res;
73}
74
75/* Write a double word */
Takashi Iwai029d64b2005-11-17 14:34:36 +010076void snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 unsigned long flags;
79 spin_lock_irqsave(&emu->reg_lock, flags);
80 if (reg != emu->last_reg) {
81 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
82 emu->last_reg = reg;
83 }
84 outw((unsigned short)val, port); /* Send low word of data */
85 outw((unsigned short)(val>>16), port+2); /* Send high word of data */
86 spin_unlock_irqrestore(&emu->reg_lock, flags);
87}
88
89/* Read a double word */
Takashi Iwai029d64b2005-11-17 14:34:36 +010090unsigned int snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070091{
92 unsigned short low;
93 unsigned int res;
94 unsigned long flags;
95 spin_lock_irqsave(&emu->reg_lock, flags);
96 if (reg != emu->last_reg) {
97 outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
98 emu->last_reg = reg;
99 }
100 low = inw(port); /* Read low word of data */
101 res = low + (inw(port+2) << 16);
102 spin_unlock_irqrestore(&emu->reg_lock, flags);
103 return res;
104}
105
106/*
107 * Set up / close a channel to be used for DMA.
108 */
109/*exported*/ void
Takashi Iwai029d64b2005-11-17 14:34:36 +0100110snd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0;
113 mode &= EMU8000_RAM_MODE_MASK;
114 if (mode == EMU8000_RAM_CLOSE) {
115 EMU8000_CCCA_WRITE(emu, ch, 0);
116 EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F);
117 return;
118 }
119 EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
120 EMU8000_VTFT_WRITE(emu, ch, 0);
121 EMU8000_CVCF_WRITE(emu, ch, 0);
122 EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
123 EMU8000_CPF_WRITE(emu, ch, 0x40000000);
124 EMU8000_PSST_WRITE(emu, ch, 0);
125 EMU8000_CSL_WRITE(emu, ch, 0);
126 if (mode == EMU8000_RAM_WRITE) /* DMA write */
127 EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit);
128 else /* DMA read */
129 EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit);
130}
131
132/*
133 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200134static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100135snd_emu8000_read_wait(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
137 while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
Nishanth Aravamudan8433a502005-10-24 15:02:37 +0200138 schedule_timeout_interruptible(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 if (signal_pending(current))
140 break;
141 }
142}
143
144/*
145 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200146static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100147snd_emu8000_write_wait(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
149 while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
Nishanth Aravamudan8433a502005-10-24 15:02:37 +0200150 schedule_timeout_interruptible(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 if (signal_pending(current))
152 break;
153 }
154}
155
156/*
157 * detect a card at the given port
158 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200159static int __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100160snd_emu8000_detect(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161{
162 /* Initialise */
163 EMU8000_HWCF1_WRITE(emu, 0x0059);
164 EMU8000_HWCF2_WRITE(emu, 0x0020);
165 EMU8000_HWCF3_WRITE(emu, 0x0000);
166 /* Check for a recognisable emu8000 */
167 /*
168 if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c)
169 return -ENODEV;
170 */
171 if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058)
172 return -ENODEV;
173 if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
174 return -ENODEV;
175
176 snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
177 emu->port1);
178 return 0;
179}
180
181
182/*
183 * intiailize audio channels
184 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200185static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100186init_audio(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187{
188 int ch;
189
190 /* turn off envelope engines */
191 for (ch = 0; ch < EMU8000_CHANNELS; ch++)
192 EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
193
194 /* reset all other parameters to zero */
195 for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
196 EMU8000_ENVVOL_WRITE(emu, ch, 0);
197 EMU8000_ENVVAL_WRITE(emu, ch, 0);
198 EMU8000_DCYSUS_WRITE(emu, ch, 0);
199 EMU8000_ATKHLDV_WRITE(emu, ch, 0);
200 EMU8000_LFO1VAL_WRITE(emu, ch, 0);
201 EMU8000_ATKHLD_WRITE(emu, ch, 0);
202 EMU8000_LFO2VAL_WRITE(emu, ch, 0);
203 EMU8000_IP_WRITE(emu, ch, 0);
204 EMU8000_IFATN_WRITE(emu, ch, 0);
205 EMU8000_PEFE_WRITE(emu, ch, 0);
206 EMU8000_FMMOD_WRITE(emu, ch, 0);
207 EMU8000_TREMFRQ_WRITE(emu, ch, 0);
208 EMU8000_FM2FRQ2_WRITE(emu, ch, 0);
209 EMU8000_PTRX_WRITE(emu, ch, 0);
210 EMU8000_VTFT_WRITE(emu, ch, 0);
211 EMU8000_PSST_WRITE(emu, ch, 0);
212 EMU8000_CSL_WRITE(emu, ch, 0);
213 EMU8000_CCCA_WRITE(emu, ch, 0);
214 }
215
216 for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
217 EMU8000_CPF_WRITE(emu, ch, 0);
218 EMU8000_CVCF_WRITE(emu, ch, 0);
219 }
220}
221
222
223/*
224 * initialize DMA address
225 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200226static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100227init_dma(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 EMU8000_SMALR_WRITE(emu, 0);
230 EMU8000_SMARR_WRITE(emu, 0);
231 EMU8000_SMALW_WRITE(emu, 0);
232 EMU8000_SMARW_WRITE(emu, 0);
233}
234
235/*
236 * initialization arrays; from ADIP
237 */
238static unsigned short init1[128] /*__devinitdata*/ = {
239 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
240 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
241 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
242 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30,
243
244 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330,
245 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730,
246 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30,
247 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30,
248
249 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330,
250 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730,
251 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30,
252 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30,
253
254 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330,
255 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730,
256 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30,
257 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
258};
259
260static unsigned short init2[128] /*__devinitdata*/ = {
261 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
262 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
263 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
264 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
265
266 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
267 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
268 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
269 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
270
271 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
272 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
273 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
274 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
275
276 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
277 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
278 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
279 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
280};
281
282static unsigned short init3[128] /*__devinitdata*/ = {
283 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
284 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
285 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
286 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
287
288 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
289 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
290 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
291 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
292
293 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
294 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
295 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
296 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
297
298 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
299 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
300 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
301 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
302};
303
304static unsigned short init4[128] /*__devinitdata*/ = {
305 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
306 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
307 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
308 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
309
310 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
311 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
312 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
313 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
314
315 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
316 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
317 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
318 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
319
320 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
321 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
322 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
323 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
324};
325
326/* send an initialization array
327 * Taken from the oss driver, not obvious from the doc how this
328 * is meant to work
329 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200330static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100331send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{
333 int i;
334 unsigned short *p;
335
336 p = data;
337 for (i = 0; i < size; i++, p++)
338 EMU8000_INIT1_WRITE(emu, i, *p);
339 for (i = 0; i < size; i++, p++)
340 EMU8000_INIT2_WRITE(emu, i, *p);
341 for (i = 0; i < size; i++, p++)
342 EMU8000_INIT3_WRITE(emu, i, *p);
343 for (i = 0; i < size; i++, p++)
344 EMU8000_INIT4_WRITE(emu, i, *p);
345}
346
347
348/*
349 * Send initialization arrays to start up, this just follows the
350 * initialisation sequence in the adip.
351 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200352static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100353init_arrays(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
355 send_array(emu, init1, ARRAY_SIZE(init1)/4);
356
357 msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */
358 send_array(emu, init2, ARRAY_SIZE(init2)/4);
359 send_array(emu, init3, ARRAY_SIZE(init3)/4);
360
361 EMU8000_HWCF4_WRITE(emu, 0);
362 EMU8000_HWCF5_WRITE(emu, 0x83);
363 EMU8000_HWCF6_WRITE(emu, 0x8000);
364
365 send_array(emu, init4, ARRAY_SIZE(init4)/4);
366}
367
368
369#define UNIQUE_ID1 0xa5b9
370#define UNIQUE_ID2 0x9d53
371
372/*
373 * Size the onboard memory.
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300374 * This is written so as not to need arbitrary delays after the write. It
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 * seems that the only way to do this is to use the one channel and keep
376 * reallocating between read and write.
377 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200378static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100379size_dram(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
Krzysztof Heltedf12b42010-01-04 22:23:34 +0100381 int i, size, detected_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383 if (emu->dram_checked)
384 return;
385
386 size = 0;
Krzysztof Heltedf12b42010-01-04 22:23:34 +0100387 detected_size = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389 /* write out a magic number */
390 snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
391 snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
392 EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
393 EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
394 snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
395
396 while (size < EMU8000_MAX_DRAM) {
397
Krzysztof Heltedf12b42010-01-04 22:23:34 +0100398 size += 512 * 1024; /* increment 512kbytes */
399
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 /* Write a unique data on the test address.
401 * if the address is out of range, the data is written on
402 * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is
403 * changed by this data.
404 */
405 /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
406 EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
407 EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
408 snd_emu8000_write_wait(emu);
409
410 /*
411 * read the data on the just written DRAM address
412 * if not the same then we have reached the end of ram.
413 */
414 /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
415 EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
416 /*snd_emu8000_read_wait(emu);*/
417 EMU8000_SMLD_READ(emu); /* discard stale data */
418 if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
Krzysztof Heltdb8cf332009-12-20 20:15:19 +0100419 break; /* no memory at this address */
420
Krzysztof Heltedf12b42010-01-04 22:23:34 +0100421 detected_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
423 snd_emu8000_read_wait(emu);
424
425 /*
426 * If it is the same it could be that the address just
427 * wraps back to the beginning; so check to see if the
428 * initial value has been overwritten.
429 */
430 EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
431 EMU8000_SMLD_READ(emu); /* discard stale data */
432 if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
433 break; /* we must have wrapped around */
434 snd_emu8000_read_wait(emu);
435 }
436
437 /* wait until FULL bit in SMAxW register is false */
438 for (i = 0; i < 10000; i++) {
439 if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
440 break;
Nishanth Aravamudan8433a502005-10-24 15:02:37 +0200441 schedule_timeout_interruptible(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 if (signal_pending(current))
443 break;
444 }
445 snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
446 snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
447
448 snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
Krzysztof Heltedf12b42010-01-04 22:23:34 +0100449 emu->port1, detected_size/1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
Krzysztof Heltedf12b42010-01-04 22:23:34 +0100451 emu->mem_size = detected_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 emu->dram_checked = 1;
453}
454
455
456/*
457 * Initiailise the FM section. You have to do this to use sample RAM
458 * and therefore lose 2 voices.
459 */
460/*exported*/ void
Takashi Iwai029d64b2005-11-17 14:34:36 +0100461snd_emu8000_init_fm(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462{
463 unsigned long flags;
464
465 /* Initialize the last two channels for DRAM refresh and producing
466 the reverb and chorus effects for Yamaha OPL-3 synthesizer */
467
468 /* 31: FM left channel, 0xffffe0-0xffffe8 */
469 EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
470 EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
471 EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
472 EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
473 EMU8000_CPF_WRITE(emu, 30, 0);
474 EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
475
476 /* 32: FM right channel, 0xfffff0-0xfffff8 */
477 EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
478 EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
479 EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
480 EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
481 EMU8000_CPF_WRITE(emu, 31, 0x8000);
482 EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
483
484 snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
485
486 spin_lock_irqsave(&emu->reg_lock, flags);
487 while (!(inw(EMU8000_PTR(emu)) & 0x1000))
488 ;
489 while ((inw(EMU8000_PTR(emu)) & 0x1000))
490 ;
491 spin_unlock_irqrestore(&emu->reg_lock, flags);
492 snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
493 /* this is really odd part.. */
494 outb(0x3C, EMU8000_PTR(emu));
495 outb(0, EMU8000_DATA1(emu));
496
497 /* skew volume & cutoff */
498 EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF);
499 EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF);
500}
501
502
503/*
504 * The main initialization routine.
505 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +0200506static void __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +0100507snd_emu8000_init_hw(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508{
509 int i;
510
511 emu->last_reg = 0xffff; /* reset the last register index */
512
513 /* initialize hardware configuration */
514 EMU8000_HWCF1_WRITE(emu, 0x0059);
515 EMU8000_HWCF2_WRITE(emu, 0x0020);
516
517 /* disable audio; this seems to reduce a clicking noise a bit.. */
518 EMU8000_HWCF3_WRITE(emu, 0);
519
520 /* initialize audio channels */
521 init_audio(emu);
522
523 /* initialize DMA */
524 init_dma(emu);
525
526 /* initialize init arrays */
527 init_arrays(emu);
528
529 /*
530 * Initialize the FM section of the AWE32, this is needed
531 * for DRAM refresh as well
532 */
533 snd_emu8000_init_fm(emu);
534
535 /* terminate all voices */
536 for (i = 0; i < EMU8000_DRAM_VOICES; i++)
537 EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F);
538
539 /* check DRAM memory size */
540 size_dram(emu);
541
542 /* enable audio */
543 EMU8000_HWCF3_WRITE(emu, 0x4);
544
545 /* set equzlier, chorus and reverb modes */
546 snd_emu8000_update_equalizer(emu);
547 snd_emu8000_update_chorus_mode(emu);
548 snd_emu8000_update_reverb_mode(emu);
549}
550
551
552/*----------------------------------------------------------------
553 * Bass/Treble Equalizer
554 *----------------------------------------------------------------*/
555
556static unsigned short bass_parm[12][3] = {
557 {0xD26A, 0xD36A, 0x0000}, /* -12 dB */
558 {0xD25B, 0xD35B, 0x0000}, /* -8 */
559 {0xD24C, 0xD34C, 0x0000}, /* -6 */
560 {0xD23D, 0xD33D, 0x0000}, /* -4 */
561 {0xD21F, 0xD31F, 0x0000}, /* -2 */
562 {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */
563 {0xC219, 0xC319, 0x0001}, /* +2 */
564 {0xC22A, 0xC32A, 0x0001}, /* +4 */
565 {0xC24C, 0xC34C, 0x0001}, /* +6 */
566 {0xC26E, 0xC36E, 0x0001}, /* +8 */
567 {0xC248, 0xC384, 0x0002}, /* +10 */
568 {0xC26A, 0xC36A, 0x0002}, /* +12 dB */
569};
570
571static unsigned short treble_parm[12][9] = {
572 {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
573 {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
574 {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
575 {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
576 {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
577 {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
578 {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
579 {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
580 {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
581 {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
582 {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
583 {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */
584};
585
586
587/*
588 * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
589 */
590/*exported*/ void
Takashi Iwai029d64b2005-11-17 14:34:36 +0100591snd_emu8000_update_equalizer(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593 unsigned short w;
594 int bass = emu->bass_level;
595 int treble = emu->treble_level;
596
597 if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
598 return;
599 EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]);
600 EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]);
601 EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]);
602 EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]);
603 EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]);
604 EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]);
605 EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]);
606 EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]);
607 EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]);
608 EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]);
609 w = bass_parm[bass][2] + treble_parm[treble][8];
610 EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262));
611 EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362));
612}
613
614
615/*----------------------------------------------------------------
616 * Chorus mode control
617 *----------------------------------------------------------------*/
618
619/*
620 * chorus mode parameters
621 */
622#define SNDRV_EMU8000_CHORUS_1 0
623#define SNDRV_EMU8000_CHORUS_2 1
624#define SNDRV_EMU8000_CHORUS_3 2
625#define SNDRV_EMU8000_CHORUS_4 3
626#define SNDRV_EMU8000_CHORUS_FEEDBACK 4
627#define SNDRV_EMU8000_CHORUS_FLANGER 5
628#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6
629#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7
630#define SNDRV_EMU8000_CHORUS_PREDEFINED 8
631/* user can define chorus modes up to 32 */
632#define SNDRV_EMU8000_CHORUS_NUMBERS 32
633
Takashi Iwai029d64b2005-11-17 14:34:36 +0100634struct soundfont_chorus_fx {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 unsigned short feedback; /* feedback level (0xE600-0xE6FF) */
636 unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */
637 unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */
638 unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
639 unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */
Takashi Iwai029d64b2005-11-17 14:34:36 +0100640};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
642/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
643static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
Takashi Iwai029d64b2005-11-17 14:34:36 +0100644static struct soundfont_chorus_fx chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
646 {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
647 {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
648 {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
649 {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
650 {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
651 {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */
652 {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */
653};
654
655/*exported*/ int
Takashi Iwai029d64b2005-11-17 14:34:36 +0100656snd_emu8000_load_chorus_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100658 struct soundfont_chorus_fx rec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
660 snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode);
661 return -EINVAL;
662 }
663 if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
664 return -EFAULT;
665 chorus_parm[mode] = rec;
666 chorus_defined[mode] = 1;
667 return 0;
668}
669
670/*exported*/ void
Takashi Iwai029d64b2005-11-17 14:34:36 +0100671snd_emu8000_update_chorus_mode(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672{
673 int effect = emu->chorus_mode;
674 if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
675 (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
676 return;
677 EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback);
678 EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset);
679 EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth);
680 EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
681 EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
682 EMU8000_HWCF6_WRITE(emu, 0x8000);
683 EMU8000_HWCF7_WRITE(emu, 0x0000);
684}
685
686/*----------------------------------------------------------------
687 * Reverb mode control
688 *----------------------------------------------------------------*/
689
690/*
691 * reverb mode parameters
692 */
693#define SNDRV_EMU8000_REVERB_ROOM1 0
694#define SNDRV_EMU8000_REVERB_ROOM2 1
695#define SNDRV_EMU8000_REVERB_ROOM3 2
696#define SNDRV_EMU8000_REVERB_HALL1 3
697#define SNDRV_EMU8000_REVERB_HALL2 4
698#define SNDRV_EMU8000_REVERB_PLATE 5
699#define SNDRV_EMU8000_REVERB_DELAY 6
700#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7
701#define SNDRV_EMU8000_REVERB_PREDEFINED 8
702/* user can define reverb modes up to 32 */
703#define SNDRV_EMU8000_REVERB_NUMBERS 32
704
Takashi Iwai029d64b2005-11-17 14:34:36 +0100705struct soundfont_reverb_fx {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 unsigned short parms[28];
Takashi Iwai029d64b2005-11-17 14:34:36 +0100707};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709/* reverb mode settings; write the following 28 data of 16 bit length
710 * on the corresponding ports in the reverb_cmds array
711 */
712static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
Takashi Iwai029d64b2005-11-17 14:34:36 +0100713static struct soundfont_reverb_fx reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714{{ /* room 1 */
715 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
716 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
717 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
718 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
719}},
720{{ /* room 2 */
721 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
722 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
723 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
724 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
725}},
726{{ /* room 3 */
727 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
728 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
729 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
730 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
731}},
732{{ /* hall 1 */
733 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
734 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
735 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
736 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
737}},
738{{ /* hall 2 */
739 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
740 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
741 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
742 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
743}},
744{{ /* plate */
745 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
746 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
747 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
748 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
749}},
750{{ /* delay */
751 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
752 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
753 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
754 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
755}},
756{{ /* panning delay */
757 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
758 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
759 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
760 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
761}},
762};
763
764enum { DATA1, DATA2 };
765#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1
766#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2
767#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1
768#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2
769
770static struct reverb_cmd_pair {
771 unsigned short cmd, port;
772} reverb_cmds[28] = {
773 {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
774 {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
775 {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
776 {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
777 {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
778 {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
779 {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
780};
781
782/*exported*/ int
Takashi Iwai029d64b2005-11-17 14:34:36 +0100783snd_emu8000_load_reverb_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100785 struct soundfont_reverb_fx rec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
787 if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
788 snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode);
789 return -EINVAL;
790 }
791 if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
792 return -EFAULT;
793 reverb_parm[mode] = rec;
794 reverb_defined[mode] = 1;
795 return 0;
796}
797
798/*exported*/ void
Takashi Iwai029d64b2005-11-17 14:34:36 +0100799snd_emu8000_update_reverb_mode(struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
801 int effect = emu->reverb_mode;
802 int i;
803
804 if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
805 (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
806 return;
807 for (i = 0; i < 28; i++) {
808 int port;
809 if (reverb_cmds[i].port == DATA1)
810 port = EMU8000_DATA1(emu);
811 else
812 port = EMU8000_DATA2(emu);
813 snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
814 }
815}
816
817
818/*----------------------------------------------------------------
819 * mixer interface
820 *----------------------------------------------------------------*/
821
822/*
823 * bass/treble
824 */
Takashi Iwai029d64b2005-11-17 14:34:36 +0100825static int mixer_bass_treble_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
827 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
828 uinfo->count = 1;
829 uinfo->value.integer.min = 0;
830 uinfo->value.integer.max = 11;
831 return 0;
832}
833
Takashi Iwai029d64b2005-11-17 14:34:36 +0100834static int mixer_bass_treble_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100836 struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
839 return 0;
840}
841
Takashi Iwai029d64b2005-11-17 14:34:36 +0100842static int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100844 struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 unsigned long flags;
846 int change;
847 unsigned short val1;
848
849 val1 = ucontrol->value.integer.value[0] % 12;
850 spin_lock_irqsave(&emu->control_lock, flags);
851 if (kcontrol->private_value) {
852 change = val1 != emu->treble_level;
853 emu->treble_level = val1;
854 } else {
855 change = val1 != emu->bass_level;
856 emu->bass_level = val1;
857 }
858 spin_unlock_irqrestore(&emu->control_lock, flags);
859 snd_emu8000_update_equalizer(emu);
860 return change;
861}
862
Takashi Iwai029d64b2005-11-17 14:34:36 +0100863static struct snd_kcontrol_new mixer_bass_control =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
865 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
866 .name = "Synth Tone Control - Bass",
867 .info = mixer_bass_treble_info,
868 .get = mixer_bass_treble_get,
869 .put = mixer_bass_treble_put,
870 .private_value = 0,
871};
872
Takashi Iwai029d64b2005-11-17 14:34:36 +0100873static struct snd_kcontrol_new mixer_treble_control =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874{
875 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
876 .name = "Synth Tone Control - Treble",
877 .info = mixer_bass_treble_info,
878 .get = mixer_bass_treble_get,
879 .put = mixer_bass_treble_put,
880 .private_value = 1,
881};
882
883/*
884 * chorus/reverb mode
885 */
Takashi Iwai029d64b2005-11-17 14:34:36 +0100886static int mixer_chorus_reverb_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887{
888 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
889 uinfo->count = 1;
890 uinfo->value.integer.min = 0;
891 uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1);
892 return 0;
893}
894
Takashi Iwai029d64b2005-11-17 14:34:36 +0100895static int mixer_chorus_reverb_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100897 struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
899 ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
900 return 0;
901}
902
Takashi Iwai029d64b2005-11-17 14:34:36 +0100903static int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100905 struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 unsigned long flags;
907 int change;
908 unsigned short val1;
909
910 spin_lock_irqsave(&emu->control_lock, flags);
911 if (kcontrol->private_value) {
912 val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
913 change = val1 != emu->chorus_mode;
914 emu->chorus_mode = val1;
915 } else {
916 val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
917 change = val1 != emu->reverb_mode;
918 emu->reverb_mode = val1;
919 }
920 spin_unlock_irqrestore(&emu->control_lock, flags);
921 if (change) {
922 if (kcontrol->private_value)
923 snd_emu8000_update_chorus_mode(emu);
924 else
925 snd_emu8000_update_reverb_mode(emu);
926 }
927 return change;
928}
929
Takashi Iwai029d64b2005-11-17 14:34:36 +0100930static struct snd_kcontrol_new mixer_chorus_mode_control =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931{
932 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
933 .name = "Chorus Mode",
934 .info = mixer_chorus_reverb_info,
935 .get = mixer_chorus_reverb_get,
936 .put = mixer_chorus_reverb_put,
937 .private_value = 1,
938};
939
Takashi Iwai029d64b2005-11-17 14:34:36 +0100940static struct snd_kcontrol_new mixer_reverb_mode_control =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941{
942 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
943 .name = "Reverb Mode",
944 .info = mixer_chorus_reverb_info,
945 .get = mixer_chorus_reverb_get,
946 .put = mixer_chorus_reverb_put,
947 .private_value = 0,
948};
949
950/*
951 * FM OPL3 chorus/reverb depth
952 */
Takashi Iwai029d64b2005-11-17 14:34:36 +0100953static int mixer_fm_depth_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954{
955 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
956 uinfo->count = 1;
957 uinfo->value.integer.min = 0;
958 uinfo->value.integer.max = 255;
959 return 0;
960}
961
Takashi Iwai029d64b2005-11-17 14:34:36 +0100962static int mixer_fm_depth_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100964 struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
967 return 0;
968}
969
Takashi Iwai029d64b2005-11-17 14:34:36 +0100970static int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971{
Takashi Iwai029d64b2005-11-17 14:34:36 +0100972 struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 unsigned long flags;
974 int change;
975 unsigned short val1;
976
977 val1 = ucontrol->value.integer.value[0] % 256;
978 spin_lock_irqsave(&emu->control_lock, flags);
979 if (kcontrol->private_value) {
980 change = val1 != emu->fm_chorus_depth;
981 emu->fm_chorus_depth = val1;
982 } else {
983 change = val1 != emu->fm_reverb_depth;
984 emu->fm_reverb_depth = val1;
985 }
986 spin_unlock_irqrestore(&emu->control_lock, flags);
987 if (change)
988 snd_emu8000_init_fm(emu);
989 return change;
990}
991
Takashi Iwai029d64b2005-11-17 14:34:36 +0100992static struct snd_kcontrol_new mixer_fm_chorus_depth_control =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993{
994 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
995 .name = "FM Chorus Depth",
996 .info = mixer_fm_depth_info,
997 .get = mixer_fm_depth_get,
998 .put = mixer_fm_depth_put,
999 .private_value = 1,
1000};
1001
Takashi Iwai029d64b2005-11-17 14:34:36 +01001002static struct snd_kcontrol_new mixer_fm_reverb_depth_control =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003{
1004 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1005 .name = "FM Reverb Depth",
1006 .info = mixer_fm_depth_info,
1007 .get = mixer_fm_depth_get,
1008 .put = mixer_fm_depth_put,
1009 .private_value = 0,
1010};
1011
1012
Takashi Iwai029d64b2005-11-17 14:34:36 +01001013static struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 &mixer_bass_control,
1015 &mixer_treble_control,
1016 &mixer_chorus_mode_control,
1017 &mixer_reverb_mode_control,
1018 &mixer_fm_chorus_depth_control,
1019 &mixer_fm_reverb_depth_control,
1020};
1021
1022/*
1023 * create and attach mixer elements for WaveTable treble/bass controls
1024 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +02001025static int __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +01001026snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027{
1028 int i, err = 0;
1029
Takashi Iwai622207d2008-08-08 17:11:45 +02001030 if (snd_BUG_ON(!emu || !card))
1031 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
1033 spin_lock_init(&emu->control_lock);
1034
1035 memset(emu->controls, 0, sizeof(emu->controls));
1036 for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
1037 if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
1038 goto __error;
1039 }
1040 return 0;
1041
1042__error:
1043 for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
1044 down_write(&card->controls_rwsem);
1045 if (emu->controls[i])
1046 snd_ctl_remove(card, emu->controls[i]);
1047 up_write(&card->controls_rwsem);
1048 }
1049 return err;
1050}
1051
1052
1053/*
1054 * free resources
1055 */
Takashi Iwai029d64b2005-11-17 14:34:36 +01001056static int snd_emu8000_free(struct snd_emu8000 *hw)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057{
Takashi Iwaib1d57762005-10-10 11:56:31 +02001058 release_and_free_resource(hw->res_port1);
1059 release_and_free_resource(hw->res_port2);
1060 release_and_free_resource(hw->res_port3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 kfree(hw);
1062 return 0;
1063}
1064
1065/*
1066 */
Takashi Iwai029d64b2005-11-17 14:34:36 +01001067static int snd_emu8000_dev_free(struct snd_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068{
Takashi Iwai029d64b2005-11-17 14:34:36 +01001069 struct snd_emu8000 *hw = device->device_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 return snd_emu8000_free(hw);
1071}
1072
1073/*
1074 * initialize and register emu8000 synth device.
1075 */
Takashi Iwai2e74eba2006-06-09 15:28:07 +02001076int __devinit
Takashi Iwai029d64b2005-11-17 14:34:36 +01001077snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
1078 struct snd_seq_device **awe_ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
Takashi Iwai029d64b2005-11-17 14:34:36 +01001080 struct snd_seq_device *awe;
1081 struct snd_emu8000 *hw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 int err;
Takashi Iwai029d64b2005-11-17 14:34:36 +01001083 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 .dev_free = snd_emu8000_dev_free,
1085 };
1086
1087 if (awe_ret)
1088 *awe_ret = NULL;
1089
1090 if (seq_ports <= 0)
1091 return 0;
1092
Takashi Iwai9e76a762005-09-09 14:21:17 +02001093 hw = kzalloc(sizeof(*hw), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 if (hw == NULL)
1095 return -ENOMEM;
1096 spin_lock_init(&hw->reg_lock);
1097 hw->index = index;
1098 hw->port1 = port;
1099 hw->port2 = port + 0x400;
1100 hw->port3 = port + 0x800;
1101 if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
1102 !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
1103 !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
1104 snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3);
1105 snd_emu8000_free(hw);
1106 return -EBUSY;
1107 }
1108 hw->mem_size = 0;
1109 hw->card = card;
1110 hw->seq_ports = seq_ports;
1111 hw->bass_level = 5;
1112 hw->treble_level = 9;
1113 hw->chorus_mode = 2;
1114 hw->reverb_mode = 4;
1115 hw->fm_chorus_depth = 0;
1116 hw->fm_reverb_depth = 0;
1117
1118 if (snd_emu8000_detect(hw) < 0) {
1119 snd_emu8000_free(hw);
1120 return -ENODEV;
1121 }
1122
1123 snd_emu8000_init_hw(hw);
1124 if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
1125 snd_emu8000_free(hw);
1126 return err;
1127 }
1128
1129 if ((err = snd_device_new(card, SNDRV_DEV_CODEC, hw, &ops)) < 0) {
1130 snd_emu8000_free(hw);
1131 return err;
1132 }
1133#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
1134 if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
Takashi Iwai029d64b2005-11-17 14:34:36 +01001135 sizeof(struct snd_emu8000*), &awe) >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 strcpy(awe->name, "EMU-8000");
Takashi Iwai029d64b2005-11-17 14:34:36 +01001137 *(struct snd_emu8000 **)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 }
1139#else
1140 awe = NULL;
1141#endif
1142 if (awe_ret)
1143 *awe_ret = awe;
1144
1145 return 0;
1146}
1147
1148
1149/*
1150 * exported stuff
1151 */
1152
1153EXPORT_SYMBOL(snd_emu8000_poke);
1154EXPORT_SYMBOL(snd_emu8000_peek);
1155EXPORT_SYMBOL(snd_emu8000_poke_dw);
1156EXPORT_SYMBOL(snd_emu8000_peek_dw);
1157EXPORT_SYMBOL(snd_emu8000_dma_chan);
1158EXPORT_SYMBOL(snd_emu8000_init_fm);
1159EXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
1160EXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
1161EXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
1162EXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
1163EXPORT_SYMBOL(snd_emu8000_update_equalizer);