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