Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/isa/sb/emu8000_patch.c
26425 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Patch routines for the emu8000 (AWE32/64)
4
*
5
* Copyright (C) 1999 Steve Ratcliffe
6
* Copyright (C) 1999-2000 Takashi Iwai <[email protected]>
7
*/
8
9
#include "emu8000_local.h"
10
11
#include <linux/sched/signal.h>
12
#include <linux/uaccess.h>
13
#include <linux/moduleparam.h>
14
15
static int emu8000_reset_addr;
16
module_param(emu8000_reset_addr, int, 0444);
17
MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
18
19
20
/*
21
* Open up channels.
22
*/
23
static int
24
snd_emu8000_open_dma(struct snd_emu8000 *emu, int write)
25
{
26
int i;
27
28
/* reserve all 30 voices for loading */
29
for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
30
snd_emux_lock_voice(emu->emu, i);
31
snd_emu8000_dma_chan(emu, i, write);
32
}
33
34
/* assign voice 31 and 32 to ROM */
35
EMU8000_VTFT_WRITE(emu, 30, 0);
36
EMU8000_PSST_WRITE(emu, 30, 0x1d8);
37
EMU8000_CSL_WRITE(emu, 30, 0x1e0);
38
EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
39
EMU8000_VTFT_WRITE(emu, 31, 0);
40
EMU8000_PSST_WRITE(emu, 31, 0x1d8);
41
EMU8000_CSL_WRITE(emu, 31, 0x1e0);
42
EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
43
44
return 0;
45
}
46
47
/*
48
* Close all dram channels.
49
*/
50
static void
51
snd_emu8000_close_dma(struct snd_emu8000 *emu)
52
{
53
int i;
54
55
for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
56
snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
57
snd_emux_unlock_voice(emu->emu, i);
58
}
59
}
60
61
/*
62
*/
63
64
#define BLANK_LOOP_START 4
65
#define BLANK_LOOP_END 8
66
#define BLANK_LOOP_SIZE 12
67
#define BLANK_HEAD_SIZE 48
68
69
/*
70
* Read a word from userland, taking care of conversions from
71
* 8bit samples etc.
72
*/
73
static unsigned short
74
read_word(const void __user *buf, int offset, int mode)
75
{
76
unsigned short c;
77
if (mode & SNDRV_SFNT_SAMPLE_8BITS) {
78
unsigned char cc;
79
get_user(cc, (unsigned char __user *)buf + offset);
80
c = cc << 8; /* convert 8bit -> 16bit */
81
} else {
82
#ifdef SNDRV_LITTLE_ENDIAN
83
get_user(c, (unsigned short __user *)buf + offset);
84
#else
85
unsigned short cc;
86
get_user(cc, (unsigned short __user *)buf + offset);
87
c = swab16(cc);
88
#endif
89
}
90
if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED)
91
c ^= 0x8000; /* unsigned -> signed */
92
return c;
93
}
94
95
/*
96
*/
97
static void
98
snd_emu8000_write_wait(struct snd_emu8000 *emu)
99
{
100
while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
101
schedule_timeout_interruptible(1);
102
if (signal_pending(current))
103
break;
104
}
105
}
106
107
/*
108
* write sample word data
109
*
110
* You should not have to keep resetting the address each time
111
* as the chip is supposed to step on the next address automatically.
112
* It mostly does, but during writes of some samples at random it
113
* completely loses words (every one in 16 roughly but with no
114
* obvious pattern).
115
*
116
* This is therefore much slower than need be, but is at least
117
* working.
118
*/
119
static inline void
120
write_word(struct snd_emu8000 *emu, int *offset, unsigned short data)
121
{
122
if (emu8000_reset_addr) {
123
if (emu8000_reset_addr > 1)
124
snd_emu8000_write_wait(emu);
125
EMU8000_SMALW_WRITE(emu, *offset);
126
}
127
EMU8000_SMLD_WRITE(emu, data);
128
*offset += 1;
129
}
130
131
/*
132
* Write the sample to EMU800 memory. This routine is invoked out of
133
* the generic soundfont routines as a callback.
134
*/
135
int
136
snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
137
struct snd_util_memhdr *hdr,
138
const void __user *data, long count)
139
{
140
int i;
141
int rc;
142
int offset;
143
int truesize;
144
int dram_offset, dram_start;
145
struct snd_emu8000 *emu;
146
147
emu = rec->hw;
148
if (snd_BUG_ON(!sp))
149
return -EINVAL;
150
151
/* compute true data size to be loaded */
152
truesize = sp->v.size;
153
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
154
truesize += sp->v.loopend - sp->v.loopstart;
155
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
156
truesize += BLANK_LOOP_SIZE;
157
158
sp->block = snd_util_mem_alloc(hdr, truesize * 2);
159
if (sp->block == NULL) {
160
/* not ENOMEM (for compatibility) */
161
return -ENOSPC;
162
}
163
164
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
165
if (!access_ok(data, sp->v.size))
166
return -EFAULT;
167
} else {
168
if (!access_ok(data, sp->v.size * 2))
169
return -EFAULT;
170
}
171
172
/* dram position (in word) -- mem_offset is byte */
173
dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
174
dram_start = dram_offset;
175
176
/* set the total size (store onto obsolete checksum value) */
177
sp->v.truesize = truesize * 2; /* in bytes */
178
179
snd_emux_terminate_all(emu->emu);
180
rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE);
181
if (rc)
182
return rc;
183
184
/* Set the address to start writing at */
185
snd_emu8000_write_wait(emu);
186
EMU8000_SMALW_WRITE(emu, dram_offset);
187
188
/*snd_emu8000_init_fm(emu);*/
189
190
#if 0
191
/* first block - write 48 samples for silence */
192
if (! sp->block->offset) {
193
for (i = 0; i < BLANK_HEAD_SIZE; i++) {
194
write_word(emu, &dram_offset, 0);
195
}
196
}
197
#endif
198
199
offset = 0;
200
for (i = 0; i < sp->v.size; i++) {
201
unsigned short s;
202
203
s = read_word(data, offset, sp->v.mode_flags);
204
offset++;
205
write_word(emu, &dram_offset, s);
206
207
/* we may take too long time in this loop.
208
* so give controls back to kernel if needed.
209
*/
210
cond_resched();
211
212
if (i == sp->v.loopend &&
213
(sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)))
214
{
215
int looplen = sp->v.loopend - sp->v.loopstart;
216
int k;
217
218
/* copy reverse loop */
219
for (k = 1; k <= looplen; k++) {
220
s = read_word(data, offset - k, sp->v.mode_flags);
221
write_word(emu, &dram_offset, s);
222
}
223
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
224
sp->v.loopend += looplen;
225
} else {
226
sp->v.loopstart += looplen;
227
sp->v.loopend += looplen;
228
}
229
sp->v.end += looplen;
230
}
231
}
232
233
/* if no blank loop is attached in the sample, add it */
234
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
235
for (i = 0; i < BLANK_LOOP_SIZE; i++) {
236
write_word(emu, &dram_offset, 0);
237
}
238
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
239
sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
240
sp->v.loopend = sp->v.end + BLANK_LOOP_END;
241
}
242
}
243
244
/* add dram offset */
245
sp->v.start += dram_start;
246
sp->v.end += dram_start;
247
sp->v.loopstart += dram_start;
248
sp->v.loopend += dram_start;
249
250
snd_emu8000_close_dma(emu);
251
snd_emu8000_init_fm(emu);
252
253
return 0;
254
}
255
256
/*
257
* free a sample block
258
*/
259
int
260
snd_emu8000_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
261
struct snd_util_memhdr *hdr)
262
{
263
if (sp->block) {
264
snd_util_mem_free(hdr, sp->block);
265
sp->block = NULL;
266
}
267
return 0;
268
}
269
270
271
/*
272
* sample_reset callback - terminate voices
273
*/
274
void
275
snd_emu8000_sample_reset(struct snd_emux *rec)
276
{
277
snd_emux_terminate_all(rec);
278
}
279
280