xref: /linux/sound/pci/emu10k1/emu10k1_patch.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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		3
20 
21 /*
22  * allocate a sample block and copy data from userspace
23  */
24 int
snd_emu10k1_sample_new(struct snd_emux * rec,struct snd_sf_sample * sp,struct snd_util_memhdr * hdr,const void __user * data,long count)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 	u8 fill;
30 	u32 xor;
31 	int shift;
32 	int offset;
33 	int truesize, size, blocksize;
34 	int loop_start, loop_end, loop_size, data_end, unroll;
35 	struct snd_emu10k1 *emu;
36 
37 	emu = rec->hw;
38 	if (snd_BUG_ON(!sp || !hdr))
39 		return -EINVAL;
40 
41 	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
42 		/* should instead return -ENOTSUPP; but compatibility */
43 		dev_warn(emu->card->dev,
44 			 "Emu10k1 wavetable patch %d with unsupported loop feature\n",
45 			 sp->v.sample);
46 	}
47 
48 	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
49 		shift = 0;
50 		fill = 0x80;
51 		xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080;
52 	} else {
53 		shift = 1;
54 		fill = 0;
55 		xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0;
56 	}
57 
58 	/* compute true data size to be loaded */
59 	truesize = sp->v.size + BLANK_HEAD_SIZE;
60 	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
61 		truesize += BLANK_LOOP_SIZE;
62 		/* if no blank loop is attached in the sample, add it */
63 		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
64 			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
65 			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
66 		}
67 	}
68 
69 	loop_start = sp->v.loopstart;
70 	loop_end = sp->v.loopend;
71 	loop_size = loop_end - loop_start;
72 	if (!loop_size)
73 		return -EINVAL;
74 	data_end = sp->v.end;
75 
76 	/* recalculate offset */
77 	sp->v.start += BLANK_HEAD_SIZE;
78 	sp->v.end += BLANK_HEAD_SIZE;
79 	sp->v.loopstart += BLANK_HEAD_SIZE;
80 	sp->v.loopend += BLANK_HEAD_SIZE;
81 
82 	// Automatic pre-filling of the cache does not work in the presence
83 	// of loops (*), and we don't want to fill it manually, as that is
84 	// fiddly and slow. So we unroll the loop until the loop end is
85 	// beyond the cache size.
86 	// (*) Strictly speaking, a single iteration is supported (that's
87 	// how it works when the playback engine runs), but handling this
88 	// special case is not worth it.
89 	unroll = 0;
90 	while (sp->v.loopend < 64) {
91 		truesize += loop_size;
92 		sp->v.loopstart += loop_size;
93 		sp->v.loopend += loop_size;
94 		sp->v.end += loop_size;
95 		unroll++;
96 	}
97 
98 	/* try to allocate a memory block */
99 	blocksize = truesize << shift;
100 	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
101 	if (sp->block == NULL) {
102 		dev_dbg(emu->card->dev,
103 			"synth malloc failed (size=%d)\n", blocksize);
104 		/* not ENOMEM (for compatibility with OSS) */
105 		return -ENOSPC;
106 	}
107 	/* set the total size */
108 	sp->v.truesize = blocksize;
109 
110 	/* write blank samples at head */
111 	offset = 0;
112 	size = BLANK_HEAD_SIZE << shift;
113 	snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill);
114 	offset += size;
115 
116 	/* copy provided samples */
117 	if (unroll && loop_end <= data_end) {
118 		size = loop_end << shift;
119 		if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
120 			goto faulty;
121 		offset += size;
122 
123 		data += loop_start << shift;
124 		while (--unroll > 0) {
125 			size = loop_size << shift;
126 			if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
127 				goto faulty;
128 			offset += size;
129 		}
130 
131 		size = (data_end - loop_start) << shift;
132 	} else {
133 		size = data_end << shift;
134 	}
135 	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
136 		goto faulty;
137 	offset += size;
138 
139 	/* clear rest of samples (if any) */
140 	if (offset < blocksize)
141 		snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill);
142 
143 	return 0;
144 
145 faulty:
146 	snd_emu10k1_synth_free(emu, sp->block);
147 	sp->block = NULL;
148 	return -EFAULT;
149 }
150 
151 /*
152  * free a sample block
153  */
154 int
snd_emu10k1_sample_free(struct snd_emux * rec,struct snd_sf_sample * sp,struct snd_util_memhdr * hdr)155 snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
156 			struct snd_util_memhdr *hdr)
157 {
158 	struct snd_emu10k1 *emu;
159 
160 	emu = rec->hw;
161 	if (snd_BUG_ON(!sp || !hdr))
162 		return -EINVAL;
163 
164 	if (sp->block) {
165 		snd_emu10k1_synth_free(emu, sp->block);
166 		sp->block = NULL;
167 	}
168 	return 0;
169 }
170 
171