1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ALSA timer back-end using hrtimer
4 * Copyright (C) 2008 Takashi Iwai
7 #include <linux/init.h>
8 #include <linux/slab.h>
9 #include <linux/module.h>
10 #include <linux/moduleparam.h>
11 #include <linux/hrtimer.h>
12 #include <sound/core.h>
13 #include <sound/timer.h>
15 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
16 MODULE_DESCRIPTION("ALSA hrtimer backend");
17 MODULE_LICENSE("GPL");
19 MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER
));
21 #define NANO_SEC 1000000000UL /* 10^9 in sec */
22 static unsigned int resolution
;
25 struct snd_timer
*timer
;
30 static enum hrtimer_restart
snd_hrtimer_callback(struct hrtimer
*hrt
)
32 struct snd_hrtimer
*stime
= container_of(hrt
, struct snd_hrtimer
, hrt
);
33 struct snd_timer
*t
= stime
->timer
;
36 enum hrtimer_restart ret
= HRTIMER_NORESTART
;
40 goto out
; /* fast path */
41 stime
->in_callback
= true;
43 spin_unlock(&t
->lock
);
45 /* calculate the drift */
46 delta
= ktime_sub(hrt
->base
->get_time(), hrtimer_get_expires(hrt
));
48 ticks
+= ktime_divns(delta
, ticks
* resolution
);
50 snd_timer_interrupt(stime
->timer
, ticks
);
54 hrtimer_add_expires_ns(hrt
, t
->sticks
* resolution
);
55 ret
= HRTIMER_RESTART
;
58 stime
->in_callback
= false;
60 spin_unlock(&t
->lock
);
64 static int snd_hrtimer_open(struct snd_timer
*t
)
66 struct snd_hrtimer
*stime
;
68 stime
= kzalloc(sizeof(*stime
), GFP_KERNEL
);
71 hrtimer_init(&stime
->hrt
, CLOCK_MONOTONIC
, HRTIMER_MODE_REL
);
73 stime
->hrt
.function
= snd_hrtimer_callback
;
74 t
->private_data
= stime
;
78 static int snd_hrtimer_close(struct snd_timer
*t
)
80 struct snd_hrtimer
*stime
= t
->private_data
;
83 spin_lock_irq(&t
->lock
);
84 t
->running
= 0; /* just to be sure */
85 stime
->in_callback
= 1; /* skip start/stop */
86 spin_unlock_irq(&t
->lock
);
88 hrtimer_cancel(&stime
->hrt
);
90 t
->private_data
= NULL
;
95 static int snd_hrtimer_start(struct snd_timer
*t
)
97 struct snd_hrtimer
*stime
= t
->private_data
;
99 if (stime
->in_callback
)
101 hrtimer_start(&stime
->hrt
, ns_to_ktime(t
->sticks
* resolution
),
106 static int snd_hrtimer_stop(struct snd_timer
*t
)
108 struct snd_hrtimer
*stime
= t
->private_data
;
110 if (stime
->in_callback
)
112 hrtimer_try_to_cancel(&stime
->hrt
);
116 static const struct snd_timer_hardware hrtimer_hw __initconst
= {
117 .flags
= SNDRV_TIMER_HW_AUTO
| SNDRV_TIMER_HW_WORK
,
118 .open
= snd_hrtimer_open
,
119 .close
= snd_hrtimer_close
,
120 .start
= snd_hrtimer_start
,
121 .stop
= snd_hrtimer_stop
,
128 static struct snd_timer
*mytimer
;
130 static int __init
snd_hrtimer_init(void)
132 struct snd_timer
*timer
;
135 resolution
= hrtimer_resolution
;
137 /* Create a new timer and set up the fields */
138 err
= snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER
,
143 timer
->module
= THIS_MODULE
;
144 strcpy(timer
->name
, "HR timer");
145 timer
->hw
= hrtimer_hw
;
146 timer
->hw
.resolution
= resolution
;
147 timer
->hw
.ticks
= NANO_SEC
/ resolution
;
148 timer
->max_instances
= 100; /* lower the limit */
150 err
= snd_timer_global_register(timer
);
152 snd_timer_global_free(timer
);
155 mytimer
= timer
; /* remember this */
160 static void __exit
snd_hrtimer_exit(void)
163 snd_timer_global_free(mytimer
);
168 module_init(snd_hrtimer_init
);
169 module_exit(snd_hrtimer_exit
);