2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
2007-10-15 09:50:19 +02:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-16 15:20:36 -07:00
* Lee Revell < rlrevell @ joe - job . com >
2023-07-15 18:08:39 +02:00
* Oswald Buddenhagen < oswald . buddenhagen @ gmx . de >
* Creative Labs , Inc .
2005-04-16 15:20:36 -07:00
*
2023-07-15 18:08:39 +02:00
* Routines for control of EMU10K1 chips - voice manager
2005-04-16 15:20:36 -07:00
*/
# include <linux/time.h>
2011-09-22 09:34:58 -04:00
# include <linux/export.h>
2005-04-16 15:20:36 -07:00
# include <sound/core.h>
# include <sound/emu10k1.h>
/* Previously the voice allocator started at 0 every time. The new voice
* allocator uses a round robin scheme . The next free voice is tracked in
* the card record and each allocation begins where the last left off . The
* hardware requires stereo interleaved voices be aligned to an even / odd
2023-05-18 16:09:47 +02:00
* boundary .
2005-04-16 15:20:36 -07:00
* - - rlrevell
*/
2005-11-17 14:50:13 +01:00
static int voice_alloc ( struct snd_emu10k1 * emu , int type , int number ,
2023-05-18 16:09:46 +02:00
struct snd_emu10k1_pcm * epcm , struct snd_emu10k1_voice * * rvoice )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:50:13 +01:00
struct snd_emu10k1_voice * voice ;
2023-05-18 16:09:47 +02:00
int i , j , k , skip ;
2005-04-16 15:20:36 -07:00
2023-05-18 16:09:47 +02:00
for ( i = emu - > next_free_voice , j = 0 ; j < NUM_G ; i = ( i + skip ) % NUM_G , j + = skip ) {
2009-02-05 16:08:14 +01:00
/*
2014-02-25 17:02:09 +01:00
dev_dbg ( emu - > card - > dev , " i %d j %d next free %d! \n " ,
2009-02-05 16:08:14 +01:00
i , j , emu - > next_free_voice ) ;
*/
2005-04-16 15:20:36 -07:00
/* stereo voices must be even/odd */
2023-05-18 16:09:47 +02:00
if ( ( number > 1 ) & & ( i % 2 ) ) {
skip = 1 ;
2005-04-16 15:20:36 -07:00
continue ;
}
2023-05-18 16:09:47 +02:00
2005-04-16 15:20:36 -07:00
for ( k = 0 ; k < number ; k + + ) {
2023-05-18 16:09:47 +02:00
voice = & emu - > voices [ i + k ] ;
2005-04-16 15:20:36 -07:00
if ( voice - > use ) {
2023-05-18 16:09:47 +02:00
skip = k + 1 ;
goto next ;
2005-04-16 15:20:36 -07:00
}
}
2023-05-18 16:09:47 +02:00
for ( k = 0 ; k < number ; k + + ) {
voice = & emu - > voices [ i + k ] ;
voice - > use = type ;
voice - > epcm = epcm ;
/* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */
2005-04-16 15:20:36 -07:00
}
2023-05-18 16:09:47 +02:00
voice - > last = 1 ;
* rvoice = & emu - > voices [ i ] ;
emu - > next_free_voice = ( i + number ) % NUM_G ;
return 0 ;
next : ;
2005-04-16 15:20:36 -07:00
}
2023-05-18 16:09:47 +02:00
return - ENOMEM ; // -EBUSY would have been better
2005-04-16 15:20:36 -07:00
}
2023-05-18 16:09:42 +02:00
static void voice_free ( struct snd_emu10k1 * emu ,
struct snd_emu10k1_voice * pvoice )
{
2023-05-18 16:09:44 +02:00
if ( pvoice - > dirty )
snd_emu10k1_voice_init ( emu , pvoice - > number ) ;
2023-05-18 16:09:42 +02:00
pvoice - > interrupt = NULL ;
2023-05-18 16:09:47 +02:00
pvoice - > use = pvoice - > dirty = pvoice - > last = 0 ;
2023-05-18 16:09:42 +02:00
pvoice - > epcm = NULL ;
}
2023-05-18 16:09:47 +02:00
int snd_emu10k1_voice_alloc ( struct snd_emu10k1 * emu , int type , int count , int channels ,
2023-05-18 16:09:46 +02:00
struct snd_emu10k1_pcm * epcm , struct snd_emu10k1_voice * * rvoice )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
int result ;
2008-08-08 17:12:14 +02:00
if ( snd_BUG_ON ( ! rvoice ) )
return - EINVAL ;
2023-05-18 16:09:47 +02:00
if ( snd_BUG_ON ( ! count ) )
return - EINVAL ;
if ( snd_BUG_ON ( ! channels ) )
2008-08-08 17:12:14 +02:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
2023-05-18 16:09:47 +02:00
for ( int got = 0 ; got < channels ; ) {
result = voice_alloc ( emu , type , count , epcm , & rvoice [ got ] ) ;
if ( result = = 0 ) {
got + + ;
/*
dev_dbg ( emu - > card - > dev , " voice alloc - %i, %i of %i \n " ,
rvoice [ got - 1 ] - > number , got , want ) ;
*/
continue ;
}
if ( type ! = EMU10K1_SYNTH & & emu - > get_synth_voice ) {
/* free a voice from synth */
2005-04-16 15:20:36 -07:00
result = emu - > get_synth_voice ( emu ) ;
2023-05-18 16:09:47 +02:00
if ( result > = 0 ) {
2023-05-18 16:09:42 +02:00
voice_free ( emu , & emu - > voices [ result ] ) ;
2023-05-18 16:09:47 +02:00
continue ;
}
}
for ( int i = 0 ; i < got ; i + + ) {
for ( int j = 0 ; j < count ; j + + )
voice_free ( emu , rvoice [ i ] + j ) ;
rvoice [ i ] = NULL ;
2005-04-16 15:20:36 -07:00
}
2023-05-18 16:09:47 +02:00
break ;
2005-04-16 15:20:36 -07:00
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
return result ;
}
2006-04-28 15:13:39 +02:00
EXPORT_SYMBOL ( snd_emu10k1_voice_alloc ) ;
2005-11-17 14:50:13 +01:00
int snd_emu10k1_voice_free ( struct snd_emu10k1 * emu ,
struct snd_emu10k1_voice * pvoice )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
2023-05-18 16:09:47 +02:00
int last ;
2005-04-16 15:20:36 -07:00
2008-08-08 17:12:14 +02:00
if ( snd_BUG_ON ( ! pvoice ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
2023-05-18 16:09:47 +02:00
do {
last = pvoice - > last ;
voice_free ( emu , pvoice + + ) ;
} while ( ! last ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
return 0 ;
}
2006-04-28 15:13:39 +02:00
EXPORT_SYMBOL ( snd_emu10k1_voice_free ) ;