brintos

brintos / linux-shallow public Read only

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