1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Digital Audio (PCM) abstract layer
4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
7 #include <linux/time.h>
9 #include <sound/core.h>
10 #include <sound/pcm.h>
11 #include <sound/timer.h>
13 #include "pcm_local.h"
19 void snd_pcm_timer_resolution_change(struct snd_pcm_substream
*substream
)
21 unsigned long rate
, mult
, fsize
, l
, post
;
22 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
26 if (snd_BUG_ON(!rate
))
31 fsize
= runtime
->period_size
;
32 if (snd_BUG_ON(!fsize
))
38 while ((mult
* fsize
) / fsize
!= mult
) {
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;
49 runtime
->timer_resolution
= (mult
* fsize
/ rate
) * post
;
52 static unsigned long snd_pcm_timer_resolution(struct snd_timer
* timer
)
54 struct snd_pcm_substream
*substream
;
56 substream
= timer
->private_data
;
57 return substream
->runtime
? substream
->runtime
->timer_resolution
: 0;
60 static int snd_pcm_timer_start(struct snd_timer
* timer
)
62 struct snd_pcm_substream
*substream
;
64 substream
= snd_timer_chip(timer
);
65 substream
->timer_running
= 1;
69 static int snd_pcm_timer_stop(struct snd_timer
* timer
)
71 struct snd_pcm_substream
*substream
;
73 substream
= snd_timer_chip(timer
);
74 substream
->timer_running
= 0;
78 static const struct snd_timer_hardware snd_pcm_timer
=
80 .flags
= SNDRV_TIMER_HW_AUTO
| SNDRV_TIMER_HW_SLAVE
,
83 .c_resolution
= snd_pcm_timer_resolution
,
84 .start
= snd_pcm_timer_start
,
85 .stop
= snd_pcm_timer_stop
,
92 static void snd_pcm_timer_free(struct snd_timer
*timer
)
94 struct snd_pcm_substream
*substream
= timer
->private_data
;
95 substream
->timer
= NULL
;
98 void snd_pcm_timer_init(struct snd_pcm_substream
*substream
)
100 struct snd_timer_id tid
;
101 struct snd_timer
*timer
;
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)
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
);
119 timer
->private_data
= substream
;
120 timer
->private_free
= snd_pcm_timer_free
;
121 substream
->timer
= timer
;
124 void snd_pcm_timer_done(struct snd_pcm_substream
*substream
)
126 if (substream
->timer
) {
127 snd_device_free(substream
->pcm
->card
, substream
->timer
);
128 substream
->timer
= NULL
;