2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Patch transfer callback for Emu10k1
*
* Copyright ( C ) 2000 Takashi iwai < tiwai @ suse . de >
*/
/*
* All the code for loading in a patch . There is very little that is
* chip specific here . Just the actual writing to the board .
*/
# include "emu10k1_synth_local.h"
/*
*/
# define BLANK_LOOP_START 4
# define BLANK_LOOP_END 8
# define BLANK_LOOP_SIZE 12
# define BLANK_HEAD_SIZE 32
/*
* allocate a sample block and copy data from userspace
*/
int
2005-11-17 16:50:13 +03:00
snd_emu10k1_sample_new ( struct snd_emux * rec , struct snd_sf_sample * sp ,
struct snd_util_memhdr * hdr ,
const void __user * data , long count )
2005-04-17 02:20:36 +04:00
{
int offset ;
2020-07-02 22:35:54 +03:00
int truesize , size , blocksize ;
__maybe_unused int loopsize ;
2005-04-17 02:20:36 +04:00
int loopend , sampleend ;
unsigned int start_addr ;
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * emu ;
2005-04-17 02:20:36 +04:00
emu = rec - > hw ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! sp | | ! hdr ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( sp - > v . size = = 0 ) {
2014-02-25 20:02:09 +04:00
dev_dbg ( emu - > card - > dev ,
" emu: rom font for sample %d \n " , sp - > v . sample ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* recalculate address offset */
sp - > v . end - = sp - > v . start ;
sp - > v . loopstart - = sp - > v . start ;
sp - > v . loopend - = sp - > v . start ;
sp - > v . start = 0 ;
/* some samples have invalid data. the addresses are corrected in voice info */
sampleend = sp - > v . end ;
if ( sampleend > sp - > v . size )
sampleend = sp - > v . size ;
loopend = sp - > v . loopend ;
if ( loopend > sampleend )
loopend = sampleend ;
/* be sure loop points start < end */
2018-07-17 18:00:43 +03:00
if ( sp - > v . loopstart > = sp - > v . loopend )
swap ( sp - > v . loopstart , sp - > v . loopend ) ;
2005-04-17 02:20:36 +04:00
/* compute true data size to be loaded */
truesize = sp - > v . size + BLANK_HEAD_SIZE ;
loopsize = 0 ;
#if 0 /* not supported */
if ( sp - > v . mode_flags & ( SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP ) )
loopsize = sp - > v . loopend - sp - > v . loopstart ;
truesize + = loopsize ;
# endif
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK )
truesize + = BLANK_LOOP_SIZE ;
/* try to allocate a memory block */
blocksize = truesize ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
blocksize * = 2 ;
sp - > block = snd_emu10k1_synth_alloc ( emu , blocksize ) ;
if ( sp - > block = = NULL ) {
2014-02-25 20:02:09 +04:00
dev_dbg ( emu - > card - > dev ,
" synth malloc failed (size=%d) \n " , blocksize ) ;
2005-04-17 02:20:36 +04:00
/* not ENOMEM (for compatibility with OSS) */
return - ENOSPC ;
}
/* set the total size */
sp - > v . truesize = blocksize ;
/* write blank samples at head */
offset = 0 ;
size = BLANK_HEAD_SIZE ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
size * = 2 ;
2008-08-08 19:12:14 +04:00
if ( offset + size > blocksize )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
snd_emu10k1_synth_bzero ( emu , sp - > block , offset , size ) ;
offset + = size ;
/* copy start->loopend */
size = loopend ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
size * = 2 ;
2008-08-08 19:12:14 +04:00
if ( offset + size > blocksize )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( snd_emu10k1_synth_copy_from_user ( emu , sp - > block , offset , data , size ) ) {
snd_emu10k1_synth_free ( emu , sp - > block ) ;
sp - > block = NULL ;
return - EFAULT ;
}
offset + = size ;
data + = size ;
2012-08-22 13:40:21 +04:00
#if 0 /* not supported yet */
2005-04-17 02:20:36 +04:00
/* handle reverse (or bidirectional) loop */
if ( sp - > v . mode_flags & ( SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP ) ) {
/* copy loop in reverse */
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) ) {
int woffset ;
unsigned short * wblock = ( unsigned short * ) block ;
woffset = offset / 2 ;
2008-08-08 19:12:14 +04:00
if ( offset + loopsize * 2 > blocksize )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < loopsize ; i + + )
wblock [ woffset + i ] = wblock [ woffset - i - 1 ] ;
offset + = loopsize * 2 ;
} else {
2008-08-08 19:12:14 +04:00
if ( offset + loopsize > blocksize )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < loopsize ; i + + )
block [ offset + i ] = block [ offset - i - 1 ] ;
offset + = loopsize ;
}
/* modify loop pointers */
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP ) {
sp - > v . loopend + = loopsize ;
} else {
sp - > v . loopstart + = loopsize ;
sp - > v . loopend + = loopsize ;
}
/* add sample pointer */
sp - > v . end + = loopsize ;
}
# endif
/* loopend -> sample end */
size = sp - > v . size - loopend ;
2008-08-08 19:12:14 +04:00
if ( size < 0 )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
size * = 2 ;
if ( snd_emu10k1_synth_copy_from_user ( emu , sp - > block , offset , data , size ) ) {
snd_emu10k1_synth_free ( emu , sp - > block ) ;
sp - > block = NULL ;
return - EFAULT ;
}
offset + = size ;
/* clear rest of samples (if any) */
if ( offset < blocksize )
snd_emu10k1_synth_bzero ( emu , sp - > block , offset , blocksize - offset ) ;
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK ) {
/* if no blank loop is attached in the sample, add it */
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT ) {
sp - > v . loopstart = sp - > v . end + BLANK_LOOP_START ;
sp - > v . loopend = sp - > v . end + BLANK_LOOP_END ;
}
}
#if 0 /* not supported yet */
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED ) {
/* unsigned -> signed */
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) ) {
unsigned short * wblock = ( unsigned short * ) block ;
for ( i = 0 ; i < truesize ; i + + )
wblock [ i ] ^ = 0x8000 ;
} else {
for ( i = 0 ; i < truesize ; i + + )
block [ i ] ^ = 0x80 ;
}
}
# endif
/* recalculate offset */
start_addr = BLANK_HEAD_SIZE * 2 ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
start_addr > > = 1 ;
sp - > v . start + = start_addr ;
sp - > v . end + = start_addr ;
sp - > v . loopstart + = start_addr ;
sp - > v . loopend + = start_addr ;
return 0 ;
}
/*
* free a sample block
*/
int
2005-11-17 16:50:13 +03:00
snd_emu10k1_sample_free ( struct snd_emux * rec , struct snd_sf_sample * sp ,
struct snd_util_memhdr * hdr )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * emu ;
2005-04-17 02:20:36 +04:00
emu = rec - > hw ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! sp | | ! hdr ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( sp - > block ) {
snd_emu10k1_synth_free ( emu , sp - > block ) ;
sp - > block = NULL ;
}
return 0 ;
}