xref: /OK3568_Linux_fs/kernel/sound/pci/emu10k1/emu10k1_patch.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Patch transfer callback for Emu10k1
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun /*
8*4882a593Smuzhiyun  * All the code for loading in a patch.  There is very little that is
9*4882a593Smuzhiyun  * chip specific here.  Just the actual writing to the board.
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include "emu10k1_synth_local.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun /*
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun #define BLANK_LOOP_START	4
17*4882a593Smuzhiyun #define BLANK_LOOP_END		8
18*4882a593Smuzhiyun #define BLANK_LOOP_SIZE		12
19*4882a593Smuzhiyun #define BLANK_HEAD_SIZE		32
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun /*
22*4882a593Smuzhiyun  * allocate a sample block and copy data from userspace
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun 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*4882a593Smuzhiyun snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
26*4882a593Smuzhiyun 		       struct snd_util_memhdr *hdr,
27*4882a593Smuzhiyun 		       const void __user *data, long count)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	int offset;
30*4882a593Smuzhiyun 	int truesize, size, blocksize;
31*4882a593Smuzhiyun 	__maybe_unused int loopsize;
32*4882a593Smuzhiyun 	int loopend, sampleend;
33*4882a593Smuzhiyun 	unsigned int start_addr;
34*4882a593Smuzhiyun 	struct snd_emu10k1 *emu;
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	emu = rec->hw;
37*4882a593Smuzhiyun 	if (snd_BUG_ON(!sp || !hdr))
38*4882a593Smuzhiyun 		return -EINVAL;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	if (sp->v.size == 0) {
41*4882a593Smuzhiyun 		dev_dbg(emu->card->dev,
42*4882a593Smuzhiyun 			"emu: rom font for sample %d\n", sp->v.sample);
43*4882a593Smuzhiyun 		return 0;
44*4882a593Smuzhiyun 	}
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	/* recalculate address offset */
47*4882a593Smuzhiyun 	sp->v.end -= sp->v.start;
48*4882a593Smuzhiyun 	sp->v.loopstart -= sp->v.start;
49*4882a593Smuzhiyun 	sp->v.loopend -= sp->v.start;
50*4882a593Smuzhiyun 	sp->v.start = 0;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	/* some samples have invalid data.  the addresses are corrected in voice info */
53*4882a593Smuzhiyun 	sampleend = sp->v.end;
54*4882a593Smuzhiyun 	if (sampleend > sp->v.size)
55*4882a593Smuzhiyun 		sampleend = sp->v.size;
56*4882a593Smuzhiyun 	loopend = sp->v.loopend;
57*4882a593Smuzhiyun 	if (loopend > sampleend)
58*4882a593Smuzhiyun 		loopend = sampleend;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	/* be sure loop points start < end */
61*4882a593Smuzhiyun 	if (sp->v.loopstart >= sp->v.loopend)
62*4882a593Smuzhiyun 		swap(sp->v.loopstart, sp->v.loopend);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	/* compute true data size to be loaded */
65*4882a593Smuzhiyun 	truesize = sp->v.size + BLANK_HEAD_SIZE;
66*4882a593Smuzhiyun 	loopsize = 0;
67*4882a593Smuzhiyun #if 0 /* not supported */
68*4882a593Smuzhiyun 	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
69*4882a593Smuzhiyun 		loopsize = sp->v.loopend - sp->v.loopstart;
70*4882a593Smuzhiyun 	truesize += loopsize;
71*4882a593Smuzhiyun #endif
72*4882a593Smuzhiyun 	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
73*4882a593Smuzhiyun 		truesize += BLANK_LOOP_SIZE;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	/* try to allocate a memory block */
76*4882a593Smuzhiyun 	blocksize = truesize;
77*4882a593Smuzhiyun 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
78*4882a593Smuzhiyun 		blocksize *= 2;
79*4882a593Smuzhiyun 	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
80*4882a593Smuzhiyun 	if (sp->block == NULL) {
81*4882a593Smuzhiyun 		dev_dbg(emu->card->dev,
82*4882a593Smuzhiyun 			"synth malloc failed (size=%d)\n", blocksize);
83*4882a593Smuzhiyun 		/* not ENOMEM (for compatibility with OSS) */
84*4882a593Smuzhiyun 		return -ENOSPC;
85*4882a593Smuzhiyun 	}
86*4882a593Smuzhiyun 	/* set the total size */
87*4882a593Smuzhiyun 	sp->v.truesize = blocksize;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	/* write blank samples at head */
90*4882a593Smuzhiyun 	offset = 0;
91*4882a593Smuzhiyun 	size = BLANK_HEAD_SIZE;
92*4882a593Smuzhiyun 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
93*4882a593Smuzhiyun 		size *= 2;
94*4882a593Smuzhiyun 	if (offset + size > blocksize)
95*4882a593Smuzhiyun 		return -EINVAL;
96*4882a593Smuzhiyun 	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
97*4882a593Smuzhiyun 	offset += size;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	/* copy start->loopend */
100*4882a593Smuzhiyun 	size = loopend;
101*4882a593Smuzhiyun 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
102*4882a593Smuzhiyun 		size *= 2;
103*4882a593Smuzhiyun 	if (offset + size > blocksize)
104*4882a593Smuzhiyun 		return -EINVAL;
105*4882a593Smuzhiyun 	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
106*4882a593Smuzhiyun 		snd_emu10k1_synth_free(emu, sp->block);
107*4882a593Smuzhiyun 		sp->block = NULL;
108*4882a593Smuzhiyun 		return -EFAULT;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 	offset += size;
111*4882a593Smuzhiyun 	data += size;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun #if 0 /* not supported yet */
114*4882a593Smuzhiyun 	/* handle reverse (or bidirectional) loop */
115*4882a593Smuzhiyun 	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
116*4882a593Smuzhiyun 		/* copy loop in reverse */
117*4882a593Smuzhiyun 		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
118*4882a593Smuzhiyun 			int woffset;
119*4882a593Smuzhiyun 			unsigned short *wblock = (unsigned short*)block;
120*4882a593Smuzhiyun 			woffset = offset / 2;
121*4882a593Smuzhiyun 			if (offset + loopsize * 2 > blocksize)
122*4882a593Smuzhiyun 				return -EINVAL;
123*4882a593Smuzhiyun 			for (i = 0; i < loopsize; i++)
124*4882a593Smuzhiyun 				wblock[woffset + i] = wblock[woffset - i -1];
125*4882a593Smuzhiyun 			offset += loopsize * 2;
126*4882a593Smuzhiyun 		} else {
127*4882a593Smuzhiyun 			if (offset + loopsize > blocksize)
128*4882a593Smuzhiyun 				return -EINVAL;
129*4882a593Smuzhiyun 			for (i = 0; i < loopsize; i++)
130*4882a593Smuzhiyun 				block[offset + i] = block[offset - i -1];
131*4882a593Smuzhiyun 			offset += loopsize;
132*4882a593Smuzhiyun 		}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 		/* modify loop pointers */
135*4882a593Smuzhiyun 		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
136*4882a593Smuzhiyun 			sp->v.loopend += loopsize;
137*4882a593Smuzhiyun 		} else {
138*4882a593Smuzhiyun 			sp->v.loopstart += loopsize;
139*4882a593Smuzhiyun 			sp->v.loopend += loopsize;
140*4882a593Smuzhiyun 		}
141*4882a593Smuzhiyun 		/* add sample pointer */
142*4882a593Smuzhiyun 		sp->v.end += loopsize;
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun #endif
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	/* loopend -> sample end */
147*4882a593Smuzhiyun 	size = sp->v.size - loopend;
148*4882a593Smuzhiyun 	if (size < 0)
149*4882a593Smuzhiyun 		return -EINVAL;
150*4882a593Smuzhiyun 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
151*4882a593Smuzhiyun 		size *= 2;
152*4882a593Smuzhiyun 	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
153*4882a593Smuzhiyun 		snd_emu10k1_synth_free(emu, sp->block);
154*4882a593Smuzhiyun 		sp->block = NULL;
155*4882a593Smuzhiyun 		return -EFAULT;
156*4882a593Smuzhiyun 	}
157*4882a593Smuzhiyun 	offset += size;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	/* clear rest of samples (if any) */
160*4882a593Smuzhiyun 	if (offset < blocksize)
161*4882a593Smuzhiyun 		snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
164*4882a593Smuzhiyun 		/* if no blank loop is attached in the sample, add it */
165*4882a593Smuzhiyun 		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
166*4882a593Smuzhiyun 			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
167*4882a593Smuzhiyun 			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
168*4882a593Smuzhiyun 		}
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun #if 0 /* not supported yet */
172*4882a593Smuzhiyun 	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
173*4882a593Smuzhiyun 		/* unsigned -> signed */
174*4882a593Smuzhiyun 		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
175*4882a593Smuzhiyun 			unsigned short *wblock = (unsigned short*)block;
176*4882a593Smuzhiyun 			for (i = 0; i < truesize; i++)
177*4882a593Smuzhiyun 				wblock[i] ^= 0x8000;
178*4882a593Smuzhiyun 		} else {
179*4882a593Smuzhiyun 			for (i = 0; i < truesize; i++)
180*4882a593Smuzhiyun 				block[i] ^= 0x80;
181*4882a593Smuzhiyun 		}
182*4882a593Smuzhiyun 	}
183*4882a593Smuzhiyun #endif
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	/* recalculate offset */
186*4882a593Smuzhiyun 	start_addr = BLANK_HEAD_SIZE * 2;
187*4882a593Smuzhiyun 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
188*4882a593Smuzhiyun 		start_addr >>= 1;
189*4882a593Smuzhiyun 	sp->v.start += start_addr;
190*4882a593Smuzhiyun 	sp->v.end += start_addr;
191*4882a593Smuzhiyun 	sp->v.loopstart += start_addr;
192*4882a593Smuzhiyun 	sp->v.loopend += start_addr;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return 0;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /*
198*4882a593Smuzhiyun  * free a sample block
199*4882a593Smuzhiyun  */
200*4882a593Smuzhiyun int
snd_emu10k1_sample_free(struct snd_emux * rec,struct snd_sf_sample * sp,struct snd_util_memhdr * hdr)201*4882a593Smuzhiyun snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
202*4882a593Smuzhiyun 			struct snd_util_memhdr *hdr)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	struct snd_emu10k1 *emu;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	emu = rec->hw;
207*4882a593Smuzhiyun 	if (snd_BUG_ON(!sp || !hdr))
208*4882a593Smuzhiyun 		return -EINVAL;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (sp->block) {
211*4882a593Smuzhiyun 		snd_emu10k1_synth_free(emu, sp->block);
212*4882a593Smuzhiyun 		sp->block = NULL;
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 	return 0;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
217