blob: 89890f24509f0312183e445782070f56ea81c01a [file] [log] [blame]
Thomas Gleixner1a59d1b82019-05-27 08:55:05 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Patch transfer callback for Emu10k1
4 *
5 * Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
7/*
8 * All the code for loading in a patch. There is very little that is
9 * chip specific here. Just the actual writing to the board.
10 */
11
12#include "emu10k1_synth_local.h"
13
14/*
15 */
16#define BLANK_LOOP_START 4
17#define BLANK_LOOP_END 8
18#define BLANK_LOOP_SIZE 12
19#define BLANK_HEAD_SIZE 32
20
21/*
22 * allocate a sample block and copy data from userspace
23 */
24int
Takashi Iwaieb4698f2005-11-17 14:50:13 +010025snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
26 struct snd_util_memhdr *hdr,
27 const void __user *data, long count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
29 int offset;
Pierre-Louis Bossart518fe502020-07-02 14:35:54 -050030 int truesize, size, blocksize;
31 __maybe_unused int loopsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 int loopend, sampleend;
33 unsigned int start_addr;
Takashi Iwaieb4698f2005-11-17 14:50:13 +010034 struct snd_emu10k1 *emu;
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36 emu = rec->hw;
Takashi Iwaida3cec32008-08-08 17:12:14 +020037 if (snd_BUG_ON(!sp || !hdr))
38 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40 if (sp->v.size == 0) {
Takashi Iwai6f002b02014-02-25 17:02:09 +010041 dev_dbg(emu->card->dev,
42 "emu: rom font for sample %d\n", sp->v.sample);
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 return 0;
44 }
45
46 /* recalculate address offset */
47 sp->v.end -= sp->v.start;
48 sp->v.loopstart -= sp->v.start;
49 sp->v.loopend -= sp->v.start;
50 sp->v.start = 0;
51
52 /* some samples have invalid data. the addresses are corrected in voice info */
53 sampleend = sp->v.end;
54 if (sampleend > sp->v.size)
55 sampleend = sp->v.size;
56 loopend = sp->v.loopend;
57 if (loopend > sampleend)
58 loopend = sampleend;
59
60 /* be sure loop points start < end */
Gustavo A. R. Silvae2d2f242018-07-17 10:00:43 -050061 if (sp->v.loopstart >= sp->v.loopend)
62 swap(sp->v.loopstart, sp->v.loopend);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
64 /* compute true data size to be loaded */
65 truesize = sp->v.size + BLANK_HEAD_SIZE;
66 loopsize = 0;
67#if 0 /* not supported */
68 if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
69 loopsize = sp->v.loopend - sp->v.loopstart;
70 truesize += loopsize;
71#endif
72 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
73 truesize += BLANK_LOOP_SIZE;
74
75 /* try to allocate a memory block */
76 blocksize = truesize;
77 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
78 blocksize *= 2;
79 sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
80 if (sp->block == NULL) {
Takashi Iwai6f002b02014-02-25 17:02:09 +010081 dev_dbg(emu->card->dev,
82 "synth malloc failed (size=%d)\n", blocksize);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 /* not ENOMEM (for compatibility with OSS) */
84 return -ENOSPC;
85 }
86 /* set the total size */
87 sp->v.truesize = blocksize;
88
89 /* write blank samples at head */
90 offset = 0;
91 size = BLANK_HEAD_SIZE;
92 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
93 size *= 2;
Takashi Iwaida3cec32008-08-08 17:12:14 +020094 if (offset + size > blocksize)
95 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
97 offset += size;
98
99 /* copy start->loopend */
100 size = loopend;
101 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
102 size *= 2;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200103 if (offset + size > blocksize)
104 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
106 snd_emu10k1_synth_free(emu, sp->block);
107 sp->block = NULL;
108 return -EFAULT;
109 }
110 offset += size;
111 data += size;
112
Masanari Iidad7558142012-08-22 18:40:21 +0900113#if 0 /* not supported yet */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 /* handle reverse (or bidirectional) loop */
115 if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
116 /* copy loop in reverse */
117 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
118 int woffset;
119 unsigned short *wblock = (unsigned short*)block;
120 woffset = offset / 2;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200121 if (offset + loopsize * 2 > blocksize)
122 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 for (i = 0; i < loopsize; i++)
124 wblock[woffset + i] = wblock[woffset - i -1];
125 offset += loopsize * 2;
126 } else {
Takashi Iwaida3cec32008-08-08 17:12:14 +0200127 if (offset + loopsize > blocksize)
128 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 for (i = 0; i < loopsize; i++)
130 block[offset + i] = block[offset - i -1];
131 offset += loopsize;
132 }
133
134 /* modify loop pointers */
135 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
136 sp->v.loopend += loopsize;
137 } else {
138 sp->v.loopstart += loopsize;
139 sp->v.loopend += loopsize;
140 }
141 /* add sample pointer */
142 sp->v.end += loopsize;
143 }
144#endif
145
146 /* loopend -> sample end */
147 size = sp->v.size - loopend;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200148 if (size < 0)
149 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
151 size *= 2;
152 if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
153 snd_emu10k1_synth_free(emu, sp->block);
154 sp->block = NULL;
155 return -EFAULT;
156 }
157 offset += size;
158
159 /* clear rest of samples (if any) */
160 if (offset < blocksize)
161 snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
162
163 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
164 /* if no blank loop is attached in the sample, add it */
165 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
166 sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
167 sp->v.loopend = sp->v.end + BLANK_LOOP_END;
168 }
169 }
170
171#if 0 /* not supported yet */
172 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
173 /* unsigned -> signed */
174 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
175 unsigned short *wblock = (unsigned short*)block;
176 for (i = 0; i < truesize; i++)
177 wblock[i] ^= 0x8000;
178 } else {
179 for (i = 0; i < truesize; i++)
180 block[i] ^= 0x80;
181 }
182 }
183#endif
184
185 /* recalculate offset */
186 start_addr = BLANK_HEAD_SIZE * 2;
187 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
188 start_addr >>= 1;
189 sp->v.start += start_addr;
190 sp->v.end += start_addr;
191 sp->v.loopstart += start_addr;
192 sp->v.loopend += start_addr;
193
194 return 0;
195}
196
197/*
198 * free a sample block
199 */
200int
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100201snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
202 struct snd_util_memhdr *hdr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100204 struct snd_emu10k1 *emu;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
206 emu = rec->hw;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200207 if (snd_BUG_ON(!sp || !hdr))
208 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
210 if (sp->block) {
211 snd_emu10k1_synth_free(emu, sp->block);
212 sp->block = NULL;
213 }
214 return 0;
215}
216