Merge branch 'work.sparc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / sound / pci / emu10k1 / emu10k1_patch.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Patch transfer callback for Emu10k1
4  *
5  *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
6  */
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  */
24 int
25 snd_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)
28 {
29         int offset;
30         int truesize, size, blocksize;
31         __maybe_unused int loopsize;
32         int loopend, sampleend;
33         unsigned int start_addr;
34         struct snd_emu10k1 *emu;
35
36         emu = rec->hw;
37         if (snd_BUG_ON(!sp || !hdr))
38                 return -EINVAL;
39
40         if (sp->v.size == 0) {
41                 dev_dbg(emu->card->dev,
42                         "emu: rom font for sample %d\n", sp->v.sample);
43                 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 */
61         if (sp->v.loopstart >= sp->v.loopend)
62                 swap(sp->v.loopstart, sp->v.loopend);
63
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) {
81                 dev_dbg(emu->card->dev,
82                         "synth malloc failed (size=%d)\n", blocksize);
83                 /* 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;
94         if (offset + size > blocksize)
95                 return -EINVAL;
96         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;
103         if (offset + size > blocksize)
104                 return -EINVAL;
105         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
113 #if 0 /* not supported yet */
114         /* 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;
121                         if (offset + loopsize * 2 > blocksize)
122                                 return -EINVAL;
123                         for (i = 0; i < loopsize; i++)
124                                 wblock[woffset + i] = wblock[woffset - i -1];
125                         offset += loopsize * 2;
126                 } else {
127                         if (offset + loopsize > blocksize)
128                                 return -EINVAL;
129                         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;
148         if (size < 0)
149                 return -EINVAL;
150         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  */
200 int
201 snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
202                         struct snd_util_memhdr *hdr)
203 {
204         struct snd_emu10k1 *emu;
205
206         emu = rec->hw;
207         if (snd_BUG_ON(!sp || !hdr))
208                 return -EINVAL;
209
210         if (sp->block) {
211                 snd_emu10k1_synth_free(emu, sp->block);
212                 sp->block = NULL;
213         }
214         return 0;
215 }
216