Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later 2 : : /* 3 : : * Digital Audio (PCM) abstract layer 4 : : * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 : : */ 6 : : 7 : : #include <linux/time.h> 8 : : #include <linux/gcd.h> 9 : : #include <sound/core.h> 10 : : #include <sound/pcm.h> 11 : : #include <sound/timer.h> 12 : : 13 : : #include "pcm_local.h" 14 : : 15 : : /* 16 : : * Timer functions 17 : : */ 18 : : 19 : 0 : void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) 20 : : { 21 : : unsigned long rate, mult, fsize, l, post; 22 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 23 : : 24 : : mult = 1000000000; 25 : 0 : rate = runtime->rate; 26 : 0 : if (snd_BUG_ON(!rate)) 27 : : return; 28 : 0 : l = gcd(mult, rate); 29 : 0 : mult /= l; 30 : 0 : rate /= l; 31 : 0 : fsize = runtime->period_size; 32 : 0 : if (snd_BUG_ON(!fsize)) 33 : : return; 34 : 0 : l = gcd(rate, fsize); 35 : 0 : rate /= l; 36 : 0 : fsize /= l; 37 : : post = 1; 38 : 0 : while ((mult * fsize) / fsize != mult) { 39 : 0 : mult /= 2; 40 : 0 : post *= 2; 41 : : } 42 : 0 : if (rate == 0) { 43 : 0 : pcm_err(substream->pcm, 44 : : "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", 45 : : runtime->rate, runtime->period_size); 46 : 0 : runtime->timer_resolution = -1; 47 : 0 : return; 48 : : } 49 : 0 : runtime->timer_resolution = (mult * fsize / rate) * post; 50 : : } 51 : : 52 : 0 : static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) 53 : : { 54 : : struct snd_pcm_substream *substream; 55 : : 56 : 0 : substream = timer->private_data; 57 : 0 : return substream->runtime ? substream->runtime->timer_resolution : 0; 58 : : } 59 : : 60 : 0 : static int snd_pcm_timer_start(struct snd_timer * timer) 61 : : { 62 : : struct snd_pcm_substream *substream; 63 : : 64 : 0 : substream = snd_timer_chip(timer); 65 : 0 : substream->timer_running = 1; 66 : 0 : return 0; 67 : : } 68 : : 69 : 0 : static int snd_pcm_timer_stop(struct snd_timer * timer) 70 : : { 71 : : struct snd_pcm_substream *substream; 72 : : 73 : 0 : substream = snd_timer_chip(timer); 74 : 0 : substream->timer_running = 0; 75 : 0 : return 0; 76 : : } 77 : : 78 : : static struct snd_timer_hardware snd_pcm_timer = 79 : : { 80 : : .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE, 81 : : .resolution = 0, 82 : : .ticks = 1, 83 : : .c_resolution = snd_pcm_timer_resolution, 84 : : .start = snd_pcm_timer_start, 85 : : .stop = snd_pcm_timer_stop, 86 : : }; 87 : : 88 : : /* 89 : : * Init functions 90 : : */ 91 : : 92 : 0 : static void snd_pcm_timer_free(struct snd_timer *timer) 93 : : { 94 : 0 : struct snd_pcm_substream *substream = timer->private_data; 95 : 0 : substream->timer = NULL; 96 : 0 : } 97 : : 98 : 3 : void snd_pcm_timer_init(struct snd_pcm_substream *substream) 99 : : { 100 : : struct snd_timer_id tid; 101 : : struct snd_timer *timer; 102 : : 103 : 3 : tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 104 : 3 : tid.dev_class = SNDRV_TIMER_CLASS_PCM; 105 : 3 : tid.card = substream->pcm->card->number; 106 : 3 : tid.device = substream->pcm->device; 107 : 3 : tid.subdevice = (substream->number << 1) | (substream->stream & 1); 108 : 3 : if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) 109 : 0 : return; 110 : 3 : sprintf(timer->name, "PCM %s %i-%i-%i", 111 : 3 : substream->stream == SNDRV_PCM_STREAM_CAPTURE ? 112 : : "capture" : "playback", 113 : : tid.card, tid.device, tid.subdevice); 114 : 3 : timer->hw = snd_pcm_timer; 115 : 3 : if (snd_device_register(timer->card, timer) < 0) { 116 : 0 : snd_device_free(timer->card, timer); 117 : 0 : return; 118 : : } 119 : 3 : timer->private_data = substream; 120 : 3 : timer->private_free = snd_pcm_timer_free; 121 : 3 : substream->timer = timer; 122 : : } 123 : : 124 : 0 : void snd_pcm_timer_done(struct snd_pcm_substream *substream) 125 : : { 126 : 0 : if (substream->timer) { 127 : 0 : snd_device_free(substream->pcm->card, substream->timer); 128 : 0 : substream->timer = NULL; 129 : : } 130 : 0 : }