linux/sound/core/pcm_timer.c
<<
>>
Prefs
   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
  19void 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
  52static 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
  60static 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
  69static 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
  78static 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
  92static 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
  98void 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                        substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
 112                                "capture" : "playback",
 113                        tid.card, tid.device, tid.subdevice);
 114        timer->hw = snd_pcm_timer;
 115        if (snd_device_register(timer->card, timer) < 0) {
 116                snd_device_free(timer->card, timer);
 117                return;
 118        }
 119        timer->private_data = substream;
 120        timer->private_free = snd_pcm_timer_free;
 121        substream->timer = timer;
 122}
 123
 124void snd_pcm_timer_done(struct snd_pcm_substream *substream)
 125{
 126        if (substream->timer) {
 127                snd_device_free(substream->pcm->card, substream->timer);
 128                substream->timer = NULL;
 129        }
 130}
 131