Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later 2 : : /* 3 : : * ALSA timer back-end using hrtimer 4 : : * Copyright (C) 2008 Takashi Iwai 5 : : */ 6 : : 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> 14 : : 15 : : MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 16 : : MODULE_DESCRIPTION("ALSA hrtimer backend"); 17 : : MODULE_LICENSE("GPL"); 18 : : 19 : : MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER)); 20 : : 21 : : #define NANO_SEC 1000000000UL /* 10^9 in sec */ 22 : : static unsigned int resolution; 23 : : 24 : : struct snd_hrtimer { 25 : : struct snd_timer *timer; 26 : : struct hrtimer hrt; 27 : : bool in_callback; 28 : : }; 29 : : 30 : 0 : static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) 31 : : { 32 : 0 : struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); 33 : 0 : struct snd_timer *t = stime->timer; 34 : 0 : ktime_t delta; 35 : 0 : unsigned long ticks; 36 : 0 : enum hrtimer_restart ret = HRTIMER_NORESTART; 37 : : 38 : 0 : spin_lock(&t->lock); 39 [ # # ]: 0 : if (!t->running) 40 : 0 : goto out; /* fast path */ 41 : 0 : stime->in_callback = true; 42 : 0 : ticks = t->sticks; 43 : 0 : spin_unlock(&t->lock); 44 : : 45 : : /* calculate the drift */ 46 [ # # ]: 0 : delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt)); 47 [ # # ]: 0 : if (delta > 0) 48 [ # # ]: 0 : ticks += ktime_divns(delta, ticks * resolution); 49 : : 50 : 0 : snd_timer_interrupt(stime->timer, ticks); 51 : : 52 : 0 : spin_lock(&t->lock); 53 [ # # ]: 0 : if (t->running) { 54 : 0 : hrtimer_add_expires_ns(hrt, t->sticks * resolution); 55 : 0 : ret = HRTIMER_RESTART; 56 : : } 57 : : 58 : 0 : stime->in_callback = false; 59 : 0 : out: 60 : 0 : spin_unlock(&t->lock); 61 : 0 : return ret; 62 : : } 63 : : 64 : 0 : static int snd_hrtimer_open(struct snd_timer *t) 65 : : { 66 : 0 : struct snd_hrtimer *stime; 67 : : 68 : 0 : stime = kzalloc(sizeof(*stime), GFP_KERNEL); 69 [ # # ]: 0 : if (!stime) 70 : : return -ENOMEM; 71 : 0 : hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 72 : 0 : stime->timer = t; 73 : 0 : stime->hrt.function = snd_hrtimer_callback; 74 : 0 : t->private_data = stime; 75 : 0 : return 0; 76 : : } 77 : : 78 : 0 : static int snd_hrtimer_close(struct snd_timer *t) 79 : : { 80 : 0 : struct snd_hrtimer *stime = t->private_data; 81 : : 82 [ # # ]: 0 : if (stime) { 83 : 0 : spin_lock_irq(&t->lock); 84 : 0 : t->running = 0; /* just to be sure */ 85 : 0 : stime->in_callback = 1; /* skip start/stop */ 86 : 0 : spin_unlock_irq(&t->lock); 87 : : 88 : 0 : hrtimer_cancel(&stime->hrt); 89 : 0 : kfree(stime); 90 : 0 : t->private_data = NULL; 91 : : } 92 : 0 : return 0; 93 : : } 94 : : 95 : 0 : static int snd_hrtimer_start(struct snd_timer *t) 96 : : { 97 : 0 : struct snd_hrtimer *stime = t->private_data; 98 : : 99 [ # # ]: 0 : if (stime->in_callback) 100 : : return 0; 101 : 0 : hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), 102 : : HRTIMER_MODE_REL); 103 : 0 : return 0; 104 : : } 105 : : 106 : 0 : static int snd_hrtimer_stop(struct snd_timer *t) 107 : : { 108 : 0 : struct snd_hrtimer *stime = t->private_data; 109 : : 110 [ # # ]: 0 : if (stime->in_callback) 111 : : return 0; 112 : 0 : hrtimer_try_to_cancel(&stime->hrt); 113 : 0 : return 0; 114 : : } 115 : : 116 : : static const struct snd_timer_hardware hrtimer_hw __initconst = { 117 : : .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, 118 : : .open = snd_hrtimer_open, 119 : : .close = snd_hrtimer_close, 120 : : .start = snd_hrtimer_start, 121 : : .stop = snd_hrtimer_stop, 122 : : }; 123 : : 124 : : /* 125 : : * entry functions 126 : : */ 127 : : 128 : : static struct snd_timer *mytimer; 129 : : 130 : 13 : static int __init snd_hrtimer_init(void) 131 : : { 132 : 13 : struct snd_timer *timer; 133 : 13 : int err; 134 : : 135 : 13 : resolution = hrtimer_resolution; 136 : : 137 : : /* Create a new timer and set up the fields */ 138 : 13 : err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER, 139 : : &timer); 140 [ + - ]: 13 : if (err < 0) 141 : : return err; 142 : : 143 : 13 : timer->module = THIS_MODULE; 144 : 13 : strcpy(timer->name, "HR timer"); 145 : 13 : timer->hw = hrtimer_hw; 146 : 13 : timer->hw.resolution = resolution; 147 : 13 : timer->hw.ticks = NANO_SEC / resolution; 148 : 13 : timer->max_instances = 100; /* lower the limit */ 149 : : 150 : 13 : err = snd_timer_global_register(timer); 151 [ - + ]: 13 : if (err < 0) { 152 : 0 : snd_timer_global_free(timer); 153 : 0 : return err; 154 : : } 155 : 13 : mytimer = timer; /* remember this */ 156 : : 157 : 13 : return 0; 158 : : } 159 : : 160 : 0 : static void __exit snd_hrtimer_exit(void) 161 : : { 162 [ # # ]: 0 : if (mytimer) { 163 : 0 : snd_timer_global_free(mytimer); 164 : 0 : mytimer = NULL; 165 : : } 166 : 0 : } 167 : : 168 : : module_init(snd_hrtimer_init); 169 : : module_exit(snd_hrtimer_exit);