xref: /linux/sound/core/seq/seq_timer.c (revision fefbbdfb)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *   ALSA sequencer Timer
41da177e4SLinus Torvalds  *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
5c1017a4cSJaroslav Kysela  *                              Jaroslav Kysela <perex@perex.cz>
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <sound/core.h>
91da177e4SLinus Torvalds #include <linux/slab.h>
101da177e4SLinus Torvalds #include "seq_timer.h"
111da177e4SLinus Torvalds #include "seq_queue.h"
121da177e4SLinus Torvalds #include "seq_info.h"
131da177e4SLinus Torvalds 
1487ef7779SClemens Ladisch /* allowed sequencer timer frequencies, in Hz */
1587ef7779SClemens Ladisch #define MIN_FREQUENCY		10
1687ef7779SClemens Ladisch #define MAX_FREQUENCY		6250
1787ef7779SClemens Ladisch #define DEFAULT_FREQUENCY	1000
1887ef7779SClemens Ladisch 
191da177e4SLinus Torvalds #define SKEW_BASE	0x10000	/* 16bit shift */
201da177e4SLinus Torvalds 
snd_seq_timer_set_tick_resolution(struct snd_seq_timer * tmr)21a32f6674SClemens Ladisch static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
221da177e4SLinus Torvalds {
23*fefbbdfbSTakashi Iwai 	unsigned int threshold =
24*fefbbdfbSTakashi Iwai 		tmr->tempo_base == 1000 ? 1000000 : 10000;
25*fefbbdfbSTakashi Iwai 
26*fefbbdfbSTakashi Iwai 	if (tmr->tempo < threshold)
27*fefbbdfbSTakashi Iwai 		tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq;
281da177e4SLinus Torvalds 	else {
291da177e4SLinus Torvalds 		/* might overflow.. */
301da177e4SLinus Torvalds 		unsigned int s;
31a32f6674SClemens Ladisch 		s = tmr->tempo % tmr->ppq;
32*fefbbdfbSTakashi Iwai 		s = (s * tmr->tempo_base) / tmr->ppq;
33*fefbbdfbSTakashi Iwai 		tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base;
34a32f6674SClemens Ladisch 		tmr->tick.resolution += s;
351da177e4SLinus Torvalds 	}
36a32f6674SClemens Ladisch 	if (tmr->tick.resolution <= 0)
37a32f6674SClemens Ladisch 		tmr->tick.resolution = 1;
38a32f6674SClemens Ladisch 	snd_seq_timer_update_tick(&tmr->tick, 0);
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds /* create new timer (constructor) */
snd_seq_timer_new(void)42c7e0b5bfSTakashi Iwai struct snd_seq_timer *snd_seq_timer_new(void)
431da177e4SLinus Torvalds {
44c7e0b5bfSTakashi Iwai 	struct snd_seq_timer *tmr;
451da177e4SLinus Torvalds 
46ecca82b4STakashi Iwai 	tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
4724db8bbaSTakashi Iwai 	if (!tmr)
481da177e4SLinus Torvalds 		return NULL;
491da177e4SLinus Torvalds 	spin_lock_init(&tmr->lock);
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	/* reset setup to defaults */
521da177e4SLinus Torvalds 	snd_seq_timer_defaults(tmr);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	/* reset time */
551da177e4SLinus Torvalds 	snd_seq_timer_reset(tmr);
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	return tmr;
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds /* delete timer (destructor) */
snd_seq_timer_delete(struct snd_seq_timer ** tmr)61c7e0b5bfSTakashi Iwai void snd_seq_timer_delete(struct snd_seq_timer **tmr)
621da177e4SLinus Torvalds {
63c7e0b5bfSTakashi Iwai 	struct snd_seq_timer *t = *tmr;
641da177e4SLinus Torvalds 	*tmr = NULL;
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 	if (t == NULL) {
6704cc79a0STakashi Iwai 		pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
681da177e4SLinus Torvalds 		return;
691da177e4SLinus Torvalds 	}
701da177e4SLinus Torvalds 	t->running = 0;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	/* reset time */
731da177e4SLinus Torvalds 	snd_seq_timer_stop(t);
741da177e4SLinus Torvalds 	snd_seq_timer_reset(t);
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	kfree(t);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
snd_seq_timer_defaults(struct snd_seq_timer * tmr)79c7e0b5bfSTakashi Iwai void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
801da177e4SLinus Torvalds {
81aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
821da177e4SLinus Torvalds 	/* setup defaults */
831da177e4SLinus Torvalds 	tmr->ppq = 96;		/* 96 PPQ */
841da177e4SLinus Torvalds 	tmr->tempo = 500000;	/* 120 BPM */
85*fefbbdfbSTakashi Iwai 	tmr->tempo_base = 1000;	/* 1us */
86a32f6674SClemens Ladisch 	snd_seq_timer_set_tick_resolution(tmr);
871da177e4SLinus Torvalds 	tmr->running = 0;
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	tmr->type = SNDRV_SEQ_TIMER_ALSA;
901da177e4SLinus Torvalds 	tmr->alsa_id.dev_class = seq_default_timer_class;
911da177e4SLinus Torvalds 	tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
921da177e4SLinus Torvalds 	tmr->alsa_id.card = seq_default_timer_card;
931da177e4SLinus Torvalds 	tmr->alsa_id.device = seq_default_timer_device;
941da177e4SLinus Torvalds 	tmr->alsa_id.subdevice = seq_default_timer_subdevice;
951da177e4SLinus Torvalds 	tmr->preferred_resolution = seq_default_timer_resolution;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	tmr->skew = tmr->skew_base = SKEW_BASE;
982cdc7b63STakashi Iwai }
992cdc7b63STakashi Iwai 
seq_timer_reset(struct snd_seq_timer * tmr)1002cdc7b63STakashi Iwai static void seq_timer_reset(struct snd_seq_timer *tmr)
1012cdc7b63STakashi Iwai {
1022cdc7b63STakashi Iwai 	/* reset time & songposition */
1032cdc7b63STakashi Iwai 	tmr->cur_time.tv_sec = 0;
1042cdc7b63STakashi Iwai 	tmr->cur_time.tv_nsec = 0;
1052cdc7b63STakashi Iwai 
1062cdc7b63STakashi Iwai 	tmr->tick.cur_tick = 0;
1072cdc7b63STakashi Iwai 	tmr->tick.fraction = 0;
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
snd_seq_timer_reset(struct snd_seq_timer * tmr)110c7e0b5bfSTakashi Iwai void snd_seq_timer_reset(struct snd_seq_timer *tmr)
1111da177e4SLinus Torvalds {
112aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
1132cdc7b63STakashi Iwai 	seq_timer_reset(tmr);
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds /* called by timer interrupt routine. the period time since previous invocation is passed */
snd_seq_timer_interrupt(struct snd_timer_instance * timeri,unsigned long resolution,unsigned long ticks)118c7e0b5bfSTakashi Iwai static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
1191da177e4SLinus Torvalds 				    unsigned long resolution,
1201da177e4SLinus Torvalds 				    unsigned long ticks)
1211da177e4SLinus Torvalds {
122c7e0b5bfSTakashi Iwai 	struct snd_seq_queue *q = timeri->callback_data;
123c7e0b5bfSTakashi Iwai 	struct snd_seq_timer *tmr;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	if (q == NULL)
1261da177e4SLinus Torvalds 		return;
1271da177e4SLinus Torvalds 	tmr = q->timer;
1281da177e4SLinus Torvalds 	if (tmr == NULL)
1291da177e4SLinus Torvalds 		return;
130aa75a222STakashi Iwai 
131aa75a222STakashi Iwai 	scoped_guard(spinlock_irqsave, &tmr->lock) {
132aa75a222STakashi Iwai 		if (!tmr->running)
1331da177e4SLinus Torvalds 			return;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 		resolution *= ticks;
1361da177e4SLinus Torvalds 		if (tmr->skew != tmr->skew_base) {
1371da177e4SLinus Torvalds 			/* FIXME: assuming skew_base = 0x10000 */
1381da177e4SLinus Torvalds 			resolution = (resolution >> 16) * tmr->skew +
1391da177e4SLinus Torvalds 				(((resolution & 0xffff) * tmr->skew) >> 16);
1401da177e4SLinus Torvalds 		}
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 		/* update timer */
1431da177e4SLinus Torvalds 		snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 		/* calculate current tick */
1461da177e4SLinus Torvalds 		snd_seq_timer_update_tick(&tmr->tick, resolution);
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 		/* register actual time of this timer update */
1493915bf29SArnd Bergmann 		ktime_get_ts64(&tmr->last_update);
150aa75a222STakashi Iwai 	}
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	/* check queues and dispatch events */
1531da177e4SLinus Torvalds 	snd_seq_check_queue(q, 1, 0);
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds /* set current tempo */
snd_seq_timer_set_tempo(struct snd_seq_timer * tmr,int tempo)157c7e0b5bfSTakashi Iwai int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
1581da177e4SLinus Torvalds {
1597eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
1607eaa943cSTakashi Iwai 		return -EINVAL;
1611da177e4SLinus Torvalds 	if (tempo <= 0)
1621da177e4SLinus Torvalds 		return -EINVAL;
163aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
1641da177e4SLinus Torvalds 	if ((unsigned int)tempo != tmr->tempo) {
1651da177e4SLinus Torvalds 		tmr->tempo = tempo;
166a32f6674SClemens Ladisch 		snd_seq_timer_set_tick_resolution(tmr);
1671da177e4SLinus Torvalds 	}
1681da177e4SLinus Torvalds 	return 0;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
171*fefbbdfbSTakashi Iwai /* set current tempo, ppq and base in a shot */
snd_seq_timer_set_tempo_ppq(struct snd_seq_timer * tmr,int tempo,int ppq,unsigned int tempo_base)172*fefbbdfbSTakashi Iwai int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
173*fefbbdfbSTakashi Iwai 				unsigned int tempo_base)
1741da177e4SLinus Torvalds {
175671ec859STakashi Iwai 	int changed;
1761da177e4SLinus Torvalds 
1777eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
1787eaa943cSTakashi Iwai 		return -EINVAL;
179671ec859STakashi Iwai 	if (tempo <= 0 || ppq <= 0)
1801da177e4SLinus Torvalds 		return -EINVAL;
181*fefbbdfbSTakashi Iwai 	/* allow only 10ns or 1us tempo base for now */
182*fefbbdfbSTakashi Iwai 	if (tempo_base && tempo_base != 10 && tempo_base != 1000)
183*fefbbdfbSTakashi Iwai 		return -EINVAL;
184aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
1851da177e4SLinus Torvalds 	if (tmr->running && (ppq != tmr->ppq)) {
1861da177e4SLinus Torvalds 		/* refuse to change ppq on running timers */
1871da177e4SLinus Torvalds 		/* because it will upset the song position (ticks) */
18804cc79a0STakashi Iwai 		pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
1891da177e4SLinus Torvalds 		return -EBUSY;
1901da177e4SLinus Torvalds 	}
191671ec859STakashi Iwai 	changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
192671ec859STakashi Iwai 	tmr->tempo = tempo;
1931da177e4SLinus Torvalds 	tmr->ppq = ppq;
194*fefbbdfbSTakashi Iwai 	tmr->tempo_base = tempo_base ? tempo_base : 1000;
195671ec859STakashi Iwai 	if (changed)
196a32f6674SClemens Ladisch 		snd_seq_timer_set_tick_resolution(tmr);
1971da177e4SLinus Torvalds 	return 0;
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds /* set current tick position */
snd_seq_timer_set_position_tick(struct snd_seq_timer * tmr,snd_seq_tick_time_t position)201c7e0b5bfSTakashi Iwai int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
202c7e0b5bfSTakashi Iwai 				    snd_seq_tick_time_t position)
2031da177e4SLinus Torvalds {
2047eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
2057eaa943cSTakashi Iwai 		return -EINVAL;
2061da177e4SLinus Torvalds 
207aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
2081da177e4SLinus Torvalds 	tmr->tick.cur_tick = position;
2091da177e4SLinus Torvalds 	tmr->tick.fraction = 0;
2101da177e4SLinus Torvalds 	return 0;
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds /* set current real-time position */
snd_seq_timer_set_position_time(struct snd_seq_timer * tmr,snd_seq_real_time_t position)214c7e0b5bfSTakashi Iwai int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
215c7e0b5bfSTakashi Iwai 				    snd_seq_real_time_t position)
2161da177e4SLinus Torvalds {
2177eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
2187eaa943cSTakashi Iwai 		return -EINVAL;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	snd_seq_sanity_real_time(&position);
221aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
2221da177e4SLinus Torvalds 	tmr->cur_time = position;
2231da177e4SLinus Torvalds 	return 0;
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds /* set timer skew */
snd_seq_timer_set_skew(struct snd_seq_timer * tmr,unsigned int skew,unsigned int base)227c7e0b5bfSTakashi Iwai int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
228c7e0b5bfSTakashi Iwai 			   unsigned int base)
2291da177e4SLinus Torvalds {
2307eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
2317eaa943cSTakashi Iwai 		return -EINVAL;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	/* FIXME */
2341da177e4SLinus Torvalds 	if (base != SKEW_BASE) {
23504cc79a0STakashi Iwai 		pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
2361da177e4SLinus Torvalds 		return -EINVAL;
2371da177e4SLinus Torvalds 	}
238aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
2391da177e4SLinus Torvalds 	tmr->skew = skew;
2401da177e4SLinus Torvalds 	return 0;
2411da177e4SLinus Torvalds }
2421da177e4SLinus Torvalds 
snd_seq_timer_open(struct snd_seq_queue * q)243c7e0b5bfSTakashi Iwai int snd_seq_timer_open(struct snd_seq_queue *q)
2441da177e4SLinus Torvalds {
245c7e0b5bfSTakashi Iwai 	struct snd_timer_instance *t;
246c7e0b5bfSTakashi Iwai 	struct snd_seq_timer *tmr;
2471da177e4SLinus Torvalds 	char str[32];
2481da177e4SLinus Torvalds 	int err;
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	tmr = q->timer;
2517eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
2527eaa943cSTakashi Iwai 		return -EINVAL;
2531da177e4SLinus Torvalds 	if (tmr->timeri)
2541da177e4SLinus Torvalds 		return -EBUSY;
2551da177e4SLinus Torvalds 	sprintf(str, "sequencer queue %i", q->queue);
2561da177e4SLinus Torvalds 	if (tmr->type != SNDRV_SEQ_TIMER_ALSA)	/* standard ALSA timer */
2571da177e4SLinus Torvalds 		return -EINVAL;
2581da177e4SLinus Torvalds 	if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
2591da177e4SLinus Torvalds 		tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
2606a34367eSTakashi Iwai 	t = snd_timer_instance_new(str);
2616a34367eSTakashi Iwai 	if (!t)
2626a34367eSTakashi Iwai 		return -ENOMEM;
2636a34367eSTakashi Iwai 	t->callback = snd_seq_timer_interrupt;
2646a34367eSTakashi Iwai 	t->callback_data = q;
2656a34367eSTakashi Iwai 	t->flags |= SNDRV_TIMER_IFLG_AUTO;
2666a34367eSTakashi Iwai 	err = snd_timer_open(t, &tmr->alsa_id, q->queue);
2671da177e4SLinus Torvalds 	if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
2681da177e4SLinus Torvalds 		if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
2691da177e4SLinus Torvalds 		    tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
270c7e0b5bfSTakashi Iwai 			struct snd_timer_id tid;
2711da177e4SLinus Torvalds 			memset(&tid, 0, sizeof(tid));
2721da177e4SLinus Torvalds 			tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
2731da177e4SLinus Torvalds 			tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
2741da177e4SLinus Torvalds 			tid.card = -1;
2751da177e4SLinus Torvalds 			tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
2766a34367eSTakashi Iwai 			err = snd_timer_open(t, &tid, q->queue);
2771da177e4SLinus Torvalds 		}
27866efdc71STakashi Iwai 	}
2791da177e4SLinus Torvalds 	if (err < 0) {
28004cc79a0STakashi Iwai 		pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
2816a34367eSTakashi Iwai 		snd_timer_instance_free(t);
2821da177e4SLinus Torvalds 		return err;
2831da177e4SLinus Torvalds 	}
284aa75a222STakashi Iwai 	scoped_guard(spinlock_irq, &tmr->lock) {
28583e197a8STakashi Iwai 		if (tmr->timeri)
28683e197a8STakashi Iwai 			err = -EBUSY;
28783e197a8STakashi Iwai 		else
2881da177e4SLinus Torvalds 			tmr->timeri = t;
289aa75a222STakashi Iwai 	}
29083e197a8STakashi Iwai 	if (err < 0) {
29183e197a8STakashi Iwai 		snd_timer_close(t);
29283e197a8STakashi Iwai 		snd_timer_instance_free(t);
29383e197a8STakashi Iwai 		return err;
29483e197a8STakashi Iwai 	}
2951da177e4SLinus Torvalds 	return 0;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
snd_seq_timer_close(struct snd_seq_queue * q)298c7e0b5bfSTakashi Iwai int snd_seq_timer_close(struct snd_seq_queue *q)
2991da177e4SLinus Torvalds {
300c7e0b5bfSTakashi Iwai 	struct snd_seq_timer *tmr;
3012cdc7b63STakashi Iwai 	struct snd_timer_instance *t;
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	tmr = q->timer;
3047eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tmr))
3057eaa943cSTakashi Iwai 		return -EINVAL;
306aa75a222STakashi Iwai 	scoped_guard(spinlock_irq, &tmr->lock) {
3072cdc7b63STakashi Iwai 		t = tmr->timeri;
3081da177e4SLinus Torvalds 		tmr->timeri = NULL;
309aa75a222STakashi Iwai 	}
3106a34367eSTakashi Iwai 	if (t) {
3112cdc7b63STakashi Iwai 		snd_timer_close(t);
3126a34367eSTakashi Iwai 		snd_timer_instance_free(t);
3136a34367eSTakashi Iwai 	}
3141da177e4SLinus Torvalds 	return 0;
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
seq_timer_stop(struct snd_seq_timer * tmr)3172cdc7b63STakashi Iwai static int seq_timer_stop(struct snd_seq_timer *tmr)
3181da177e4SLinus Torvalds {
3191da177e4SLinus Torvalds 	if (! tmr->timeri)
3201da177e4SLinus Torvalds 		return -EINVAL;
3211da177e4SLinus Torvalds 	if (!tmr->running)
3221da177e4SLinus Torvalds 		return 0;
3231da177e4SLinus Torvalds 	tmr->running = 0;
3241da177e4SLinus Torvalds 	snd_timer_pause(tmr->timeri);
3251da177e4SLinus Torvalds 	return 0;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
snd_seq_timer_stop(struct snd_seq_timer * tmr)3282cdc7b63STakashi Iwai int snd_seq_timer_stop(struct snd_seq_timer *tmr)
3292cdc7b63STakashi Iwai {
330aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
331aa75a222STakashi Iwai 	return seq_timer_stop(tmr);
3322cdc7b63STakashi Iwai }
3332cdc7b63STakashi Iwai 
initialize_timer(struct snd_seq_timer * tmr)334c7e0b5bfSTakashi Iwai static int initialize_timer(struct snd_seq_timer *tmr)
3351da177e4SLinus Torvalds {
336c7e0b5bfSTakashi Iwai 	struct snd_timer *t;
33787ef7779SClemens Ladisch 	unsigned long freq;
33887ef7779SClemens Ladisch 
3391da177e4SLinus Torvalds 	t = tmr->timeri->timer;
34043a35428STakashi Iwai 	if (!t)
3417eaa943cSTakashi Iwai 		return -EINVAL;
3421da177e4SLinus Torvalds 
34387ef7779SClemens Ladisch 	freq = tmr->preferred_resolution;
34487ef7779SClemens Ladisch 	if (!freq)
34587ef7779SClemens Ladisch 		freq = DEFAULT_FREQUENCY;
34687ef7779SClemens Ladisch 	else if (freq < MIN_FREQUENCY)
34787ef7779SClemens Ladisch 		freq = MIN_FREQUENCY;
34887ef7779SClemens Ladisch 	else if (freq > MAX_FREQUENCY)
34987ef7779SClemens Ladisch 		freq = MAX_FREQUENCY;
35087ef7779SClemens Ladisch 
3511da177e4SLinus Torvalds 	tmr->ticks = 1;
35287ef7779SClemens Ladisch 	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
35321244e3dSTakashi Iwai 		unsigned long r = snd_timer_resolution(tmr->timeri);
3541da177e4SLinus Torvalds 		if (r) {
35587ef7779SClemens Ladisch 			tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
3561da177e4SLinus Torvalds 			if (! tmr->ticks)
3571da177e4SLinus Torvalds 				tmr->ticks = 1;
3581da177e4SLinus Torvalds 		}
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 	tmr->initialized = 1;
3611da177e4SLinus Torvalds 	return 0;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
seq_timer_start(struct snd_seq_timer * tmr)3642cdc7b63STakashi Iwai static int seq_timer_start(struct snd_seq_timer *tmr)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	if (! tmr->timeri)
3671da177e4SLinus Torvalds 		return -EINVAL;
3681da177e4SLinus Torvalds 	if (tmr->running)
3692cdc7b63STakashi Iwai 		seq_timer_stop(tmr);
3702cdc7b63STakashi Iwai 	seq_timer_reset(tmr);
3711da177e4SLinus Torvalds 	if (initialize_timer(tmr) < 0)
3721da177e4SLinus Torvalds 		return -EINVAL;
3731da177e4SLinus Torvalds 	snd_timer_start(tmr->timeri, tmr->ticks);
3741da177e4SLinus Torvalds 	tmr->running = 1;
3753915bf29SArnd Bergmann 	ktime_get_ts64(&tmr->last_update);
3761da177e4SLinus Torvalds 	return 0;
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
snd_seq_timer_start(struct snd_seq_timer * tmr)3792cdc7b63STakashi Iwai int snd_seq_timer_start(struct snd_seq_timer *tmr)
3802cdc7b63STakashi Iwai {
381aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
382aa75a222STakashi Iwai 	return seq_timer_start(tmr);
3832cdc7b63STakashi Iwai }
3842cdc7b63STakashi Iwai 
seq_timer_continue(struct snd_seq_timer * tmr)3852cdc7b63STakashi Iwai static int seq_timer_continue(struct snd_seq_timer *tmr)
3862cdc7b63STakashi Iwai {
3872cdc7b63STakashi Iwai 	if (! tmr->timeri)
3882cdc7b63STakashi Iwai 		return -EINVAL;
3892cdc7b63STakashi Iwai 	if (tmr->running)
3902cdc7b63STakashi Iwai 		return -EBUSY;
3912cdc7b63STakashi Iwai 	if (! tmr->initialized) {
3922cdc7b63STakashi Iwai 		seq_timer_reset(tmr);
3932cdc7b63STakashi Iwai 		if (initialize_timer(tmr) < 0)
3942cdc7b63STakashi Iwai 			return -EINVAL;
3952cdc7b63STakashi Iwai 	}
3962cdc7b63STakashi Iwai 	snd_timer_start(tmr->timeri, tmr->ticks);
3972cdc7b63STakashi Iwai 	tmr->running = 1;
3983915bf29SArnd Bergmann 	ktime_get_ts64(&tmr->last_update);
3992cdc7b63STakashi Iwai 	return 0;
4002cdc7b63STakashi Iwai }
4012cdc7b63STakashi Iwai 
snd_seq_timer_continue(struct snd_seq_timer * tmr)402c7e0b5bfSTakashi Iwai int snd_seq_timer_continue(struct snd_seq_timer *tmr)
4031da177e4SLinus Torvalds {
404aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
405aa75a222STakashi Iwai 	return seq_timer_continue(tmr);
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds /* return current 'real' time. use timeofday() to get better granularity. */
snd_seq_timer_get_cur_time(struct snd_seq_timer * tmr,bool adjust_ktime)409dc749779STakashi Iwai snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
410dc749779STakashi Iwai 					       bool adjust_ktime)
4111da177e4SLinus Torvalds {
4121da177e4SLinus Torvalds 	snd_seq_real_time_t cur_time;
4131da177e4SLinus Torvalds 
414aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
4151da177e4SLinus Torvalds 	cur_time = tmr->cur_time;
416dc749779STakashi Iwai 	if (adjust_ktime && tmr->running) {
4173915bf29SArnd Bergmann 		struct timespec64 tm;
4183915bf29SArnd Bergmann 
4193915bf29SArnd Bergmann 		ktime_get_ts64(&tm);
4203915bf29SArnd Bergmann 		tm = timespec64_sub(tm, tmr->last_update);
4219b50898aSTakashi Iwai 		cur_time.tv_nsec += tm.tv_nsec;
4229b50898aSTakashi Iwai 		cur_time.tv_sec += tm.tv_sec;
4231da177e4SLinus Torvalds 		snd_seq_sanity_real_time(&cur_time);
4241da177e4SLinus Torvalds 	}
4251da177e4SLinus Torvalds 	return cur_time;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds /* TODO: use interpolation on tick queue (will only be useful for very
4291da177e4SLinus Torvalds  high PPQ values) */
snd_seq_timer_get_cur_tick(struct snd_seq_timer * tmr)430c7e0b5bfSTakashi Iwai snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
4311da177e4SLinus Torvalds {
432aa75a222STakashi Iwai 	guard(spinlock_irqsave)(&tmr->lock);
433aa75a222STakashi Iwai 	return tmr->tick.cur_tick;
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 
437cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
4381da177e4SLinus Torvalds /* exported to seq_info.c */
snd_seq_info_timer_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)439c7e0b5bfSTakashi Iwai void snd_seq_info_timer_read(struct snd_info_entry *entry,
440c7e0b5bfSTakashi Iwai 			     struct snd_info_buffer *buffer)
4411da177e4SLinus Torvalds {
4421da177e4SLinus Torvalds 	int idx;
443c7e0b5bfSTakashi Iwai 	struct snd_seq_queue *q;
444c7e0b5bfSTakashi Iwai 	struct snd_seq_timer *tmr;
445c7e0b5bfSTakashi Iwai 	struct snd_timer_instance *ti;
4461da177e4SLinus Torvalds 	unsigned long resolution;
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
4491da177e4SLinus Torvalds 		q = queueptr(idx);
4501da177e4SLinus Torvalds 		if (q == NULL)
4511da177e4SLinus Torvalds 			continue;
452aa75a222STakashi Iwai 		scoped_guard(mutex, &q->timer_mutex) {
45360adcfdeSTakashi Iwai 			tmr = q->timer;
45460adcfdeSTakashi Iwai 			if (!tmr)
455aa75a222STakashi Iwai 				break;
45660adcfdeSTakashi Iwai 			ti = tmr->timeri;
45760adcfdeSTakashi Iwai 			if (!ti)
458aa75a222STakashi Iwai 				break;
4591da177e4SLinus Torvalds 			snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
4601da177e4SLinus Torvalds 			resolution = snd_timer_resolution(ti) * tmr->ticks;
4611da177e4SLinus Torvalds 			snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
4621da177e4SLinus Torvalds 			snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
463aa75a222STakashi Iwai 		}
4641da177e4SLinus Torvalds 		queuefree(q);
4651da177e4SLinus Torvalds  	}
4661da177e4SLinus Torvalds }
467cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */
46804f141a8STakashi Iwai 
469