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 void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) 20 { 21 unsigned long rate, mult, fsize, l, post; 22 struct snd_pcm_runtime *runtime = substream->runtime; 23 24 mult = 1000000000; 25 rate = runtime->rate; 26 if (snd_BUG_ON(!rate)) 27 return; 28 l = gcd(mult, rate); 29 mult /= l; 30 rate /= l; 31 fsize = runtime->period_size; 32 if (snd_BUG_ON(!fsize)) 33 return; 34 l = gcd(rate, fsize); 35 rate /= l; 36 fsize /= l; 37 post = 1; 38 while ((mult * fsize) / fsize != mult) { 39 mult /= 2; 40 post *= 2; 41 } 42 if (rate == 0) { 43 pcm_err(substream->pcm, 44 "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", 45 runtime->rate, runtime->period_size); 46 runtime->timer_resolution = -1; 47 return; 48 } 49 runtime->timer_resolution = (mult * fsize / rate) * post; 50 } 51 52 static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) 53 { 54 struct snd_pcm_substream *substream; 55 56 substream = timer->private_data; 57 return substream->runtime ? substream->runtime->timer_resolution : 0; 58 } 59 60 static int snd_pcm_timer_start(struct snd_timer * timer) 61 { 62 struct snd_pcm_substream *substream; 63 64 substream = snd_timer_chip(timer); 65 substream->timer_running = 1; 66 return 0; 67 } 68 69 static int snd_pcm_timer_stop(struct snd_timer * timer) 70 { 71 struct snd_pcm_substream *substream; 72 73 substream = snd_timer_chip(timer); 74 substream->timer_running = 0; 75 return 0; 76 } 77 78 static const 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 static void snd_pcm_timer_free(struct snd_timer *timer) 93 { 94 struct snd_pcm_substream *substream = timer->private_data; 95 substream->timer = NULL; 96 } 97 98 void snd_pcm_timer_init(struct snd_pcm_substream *substream) 99 { 100 struct snd_timer_id tid; 101 struct snd_timer *timer; 102 103 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 104 tid.dev_class = SNDRV_TIMER_CLASS_PCM; 105 tid.card = substream->pcm->card->number; 106 tid.device = substream->pcm->device; 107 tid.subdevice = (substream->number << 1) | (substream->stream & 1); 108 if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) 109 return; 110 sprintf(timer->name, "PCM %s %i-%i-%i", 111 snd_pcm_direction_name(substream->stream), 112 tid.card, tid.device, tid.subdevice); 113 timer->hw = snd_pcm_timer; 114 if (snd_device_register(timer->card, timer) < 0) { 115 snd_device_free(timer->card, timer); 116 return; 117 } 118 timer->private_data = substream; 119 timer->private_free = snd_pcm_timer_free; 120 substream->timer = timer; 121 } 122 123 void snd_pcm_timer_done(struct snd_pcm_substream *substream) 124 { 125 if (substream->timer) { 126 snd_device_free(substream->pcm->card, substream->timer); 127 substream->timer = NULL; 128 } 129 } 130