2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Digital Audio ( PCM ) abstract layer
2007-10-15 11:50:19 +04:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-17 02:20:36 +04:00
*/
# include <linux/time.h>
2009-12-22 03:36:10 +03:00
# include <linux/gcd.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/timer.h>
2017-05-30 13:39:45 +03:00
# include "pcm_local.h"
2005-04-17 02:20:36 +04:00
/*
* Timer functions
*/
2005-11-17 15:59:38 +03:00
void snd_pcm_timer_resolution_change ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
unsigned long rate , mult , fsize , l , post ;
2005-11-17 15:59:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2017-05-30 13:39:45 +03:00
mult = 1000000000 ;
2005-04-17 02:20:36 +04:00
rate = runtime - > rate ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! rate ) )
return ;
2005-04-17 02:20:36 +04:00
l = gcd ( mult , rate ) ;
mult / = l ;
rate / = l ;
fsize = runtime - > period_size ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! fsize ) )
return ;
2005-04-17 02:20:36 +04:00
l = gcd ( rate , fsize ) ;
rate / = l ;
fsize / = l ;
post = 1 ;
while ( ( mult * fsize ) / fsize ! = mult ) {
mult / = 2 ;
post * = 2 ;
}
if ( rate = = 0 ) {
2014-02-04 21:19:48 +04:00
pcm_err ( substream - > pcm ,
" pcm timer resolution out of range (rate = %u, period_size = %lu) \n " ,
runtime - > rate , runtime - > period_size ) ;
2005-04-17 02:20:36 +04:00
runtime - > timer_resolution = - 1 ;
return ;
}
runtime - > timer_resolution = ( mult * fsize / rate ) * post ;
}
2005-11-17 15:59:38 +03:00
static unsigned long snd_pcm_timer_resolution ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ;
2017-05-30 13:39:45 +03:00
2005-04-17 02:20:36 +04:00
substream = timer - > private_data ;
return substream - > runtime ? substream - > runtime - > timer_resolution : 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_timer_start ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ;
2017-05-30 13:39:45 +03:00
2005-04-17 02:20:36 +04:00
substream = snd_timer_chip ( timer ) ;
substream - > timer_running = 1 ;
return 0 ;
}
2005-11-17 15:59:38 +03:00
static int snd_pcm_timer_stop ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream ;
2017-05-30 13:39:45 +03:00
2005-04-17 02:20:36 +04:00
substream = snd_timer_chip ( timer ) ;
substream - > timer_running = 0 ;
return 0 ;
}
2020-01-03 11:16:36 +03:00
static const struct snd_timer_hardware snd_pcm_timer =
2005-04-17 02:20:36 +04:00
{
. flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE ,
. resolution = 0 ,
. ticks = 1 ,
. c_resolution = snd_pcm_timer_resolution ,
. start = snd_pcm_timer_start ,
. stop = snd_pcm_timer_stop ,
} ;
/*
* Init functions
*/
2005-11-17 15:59:38 +03:00
static void snd_pcm_timer_free ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_pcm_substream * substream = timer - > private_data ;
2005-04-17 02:20:36 +04:00
substream - > timer = NULL ;
}
2005-11-17 15:59:38 +03:00
void snd_pcm_timer_init ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:59:38 +03:00
struct snd_timer_id tid ;
struct snd_timer * timer ;
2017-05-30 13:39:45 +03:00
2005-04-17 02:20:36 +04:00
tid . dev_sclass = SNDRV_TIMER_SCLASS_NONE ;
tid . dev_class = SNDRV_TIMER_CLASS_PCM ;
tid . card = substream - > pcm - > card - > number ;
tid . device = substream - > pcm - > device ;
tid . subdevice = ( substream - > number < < 1 ) | ( substream - > stream & 1 ) ;
if ( snd_timer_new ( substream - > pcm - > card , " PCM " , & tid , & timer ) < 0 )
return ;
sprintf ( timer - > name , " PCM %s %i-%i-%i " ,
substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ?
" capture " : " playback " ,
tid . card , tid . device , tid . subdevice ) ;
timer - > hw = snd_pcm_timer ;
if ( snd_device_register ( timer - > card , timer ) < 0 ) {
snd_device_free ( timer - > card , timer ) ;
return ;
}
timer - > private_data = substream ;
timer - > private_free = snd_pcm_timer_free ;
substream - > timer = timer ;
}
2005-11-17 15:59:38 +03:00
void snd_pcm_timer_done ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
if ( substream - > timer ) {
snd_device_free ( substream - > pcm - > card , substream - > timer ) ;
substream - > timer = NULL ;
}
}