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 {
23a32f6674SClemens Ladisch if (tmr->tempo < 1000000)
24a32f6674SClemens Ladisch tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
251da177e4SLinus Torvalds else {
261da177e4SLinus Torvalds /* might overflow.. */
271da177e4SLinus Torvalds unsigned int s;
28a32f6674SClemens Ladisch s = tmr->tempo % tmr->ppq;
29a32f6674SClemens Ladisch s = (s * 1000) / tmr->ppq;
30a32f6674SClemens Ladisch tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
31a32f6674SClemens Ladisch tmr->tick.resolution += s;
321da177e4SLinus Torvalds }
33a32f6674SClemens Ladisch if (tmr->tick.resolution <= 0)
34a32f6674SClemens Ladisch tmr->tick.resolution = 1;
35a32f6674SClemens Ladisch snd_seq_timer_update_tick(&tmr->tick, 0);
361da177e4SLinus Torvalds }
371da177e4SLinus Torvalds
381da177e4SLinus Torvalds /* create new timer (constructor) */
snd_seq_timer_new(void)39c7e0b5bfSTakashi Iwai struct snd_seq_timer *snd_seq_timer_new(void)
401da177e4SLinus Torvalds {
41c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
421da177e4SLinus Torvalds
43ecca82b4STakashi Iwai tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
4424db8bbaSTakashi Iwai if (!tmr)
451da177e4SLinus Torvalds return NULL;
461da177e4SLinus Torvalds spin_lock_init(&tmr->lock);
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds /* reset setup to defaults */
491da177e4SLinus Torvalds snd_seq_timer_defaults(tmr);
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /* reset time */
521da177e4SLinus Torvalds snd_seq_timer_reset(tmr);
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds return tmr;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds
571da177e4SLinus Torvalds /* delete timer (destructor) */
snd_seq_timer_delete(struct snd_seq_timer ** tmr)58c7e0b5bfSTakashi Iwai void snd_seq_timer_delete(struct snd_seq_timer **tmr)
591da177e4SLinus Torvalds {
60c7e0b5bfSTakashi Iwai struct snd_seq_timer *t = *tmr;
611da177e4SLinus Torvalds *tmr = NULL;
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds if (t == NULL) {
6404cc79a0STakashi Iwai pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
651da177e4SLinus Torvalds return;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds t->running = 0;
681da177e4SLinus Torvalds
691da177e4SLinus Torvalds /* reset time */
701da177e4SLinus Torvalds snd_seq_timer_stop(t);
711da177e4SLinus Torvalds snd_seq_timer_reset(t);
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds kfree(t);
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds
snd_seq_timer_defaults(struct snd_seq_timer * tmr)76c7e0b5bfSTakashi Iwai void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
771da177e4SLinus Torvalds {
782cdc7b63STakashi Iwai unsigned long flags;
792cdc7b63STakashi Iwai
802cdc7b63STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
811da177e4SLinus Torvalds /* setup defaults */
821da177e4SLinus Torvalds tmr->ppq = 96; /* 96 PPQ */
831da177e4SLinus Torvalds tmr->tempo = 500000; /* 120 BPM */
84a32f6674SClemens Ladisch snd_seq_timer_set_tick_resolution(tmr);
851da177e4SLinus Torvalds tmr->running = 0;
861da177e4SLinus Torvalds
871da177e4SLinus Torvalds tmr->type = SNDRV_SEQ_TIMER_ALSA;
881da177e4SLinus Torvalds tmr->alsa_id.dev_class = seq_default_timer_class;
891da177e4SLinus Torvalds tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
901da177e4SLinus Torvalds tmr->alsa_id.card = seq_default_timer_card;
911da177e4SLinus Torvalds tmr->alsa_id.device = seq_default_timer_device;
921da177e4SLinus Torvalds tmr->alsa_id.subdevice = seq_default_timer_subdevice;
931da177e4SLinus Torvalds tmr->preferred_resolution = seq_default_timer_resolution;
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds tmr->skew = tmr->skew_base = SKEW_BASE;
962cdc7b63STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
972cdc7b63STakashi Iwai }
982cdc7b63STakashi Iwai
seq_timer_reset(struct snd_seq_timer * tmr)992cdc7b63STakashi Iwai static void seq_timer_reset(struct snd_seq_timer *tmr)
1002cdc7b63STakashi Iwai {
1012cdc7b63STakashi Iwai /* reset time & songposition */
1022cdc7b63STakashi Iwai tmr->cur_time.tv_sec = 0;
1032cdc7b63STakashi Iwai tmr->cur_time.tv_nsec = 0;
1042cdc7b63STakashi Iwai
1052cdc7b63STakashi Iwai tmr->tick.cur_tick = 0;
1062cdc7b63STakashi Iwai tmr->tick.fraction = 0;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds
snd_seq_timer_reset(struct snd_seq_timer * tmr)109c7e0b5bfSTakashi Iwai void snd_seq_timer_reset(struct snd_seq_timer *tmr)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds unsigned long flags;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds spin_lock_irqsave(&tmr->lock, flags);
1142cdc7b63STakashi Iwai seq_timer_reset(tmr);
1151da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds
1181da177e4SLinus Torvalds
1191da177e4SLinus 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)120c7e0b5bfSTakashi Iwai static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
1211da177e4SLinus Torvalds unsigned long resolution,
1221da177e4SLinus Torvalds unsigned long ticks)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds unsigned long flags;
125c7e0b5bfSTakashi Iwai struct snd_seq_queue *q = timeri->callback_data;
126c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
1271da177e4SLinus Torvalds
1281da177e4SLinus Torvalds if (q == NULL)
1291da177e4SLinus Torvalds return;
1301da177e4SLinus Torvalds tmr = q->timer;
1311da177e4SLinus Torvalds if (tmr == NULL)
1321da177e4SLinus Torvalds return;
1332cdc7b63STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
1342cdc7b63STakashi Iwai if (!tmr->running) {
1352cdc7b63STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
1361da177e4SLinus Torvalds return;
1372cdc7b63STakashi Iwai }
1381da177e4SLinus Torvalds
1391da177e4SLinus Torvalds resolution *= ticks;
1401da177e4SLinus Torvalds if (tmr->skew != tmr->skew_base) {
1411da177e4SLinus Torvalds /* FIXME: assuming skew_base = 0x10000 */
1421da177e4SLinus Torvalds resolution = (resolution >> 16) * tmr->skew +
1431da177e4SLinus Torvalds (((resolution & 0xffff) * tmr->skew) >> 16);
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds /* update timer */
1471da177e4SLinus Torvalds snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
1481da177e4SLinus Torvalds
1491da177e4SLinus Torvalds /* calculate current tick */
1501da177e4SLinus Torvalds snd_seq_timer_update_tick(&tmr->tick, resolution);
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds /* register actual time of this timer update */
1533915bf29SArnd Bergmann ktime_get_ts64(&tmr->last_update);
1541da177e4SLinus Torvalds
1551da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
1561da177e4SLinus Torvalds
1571da177e4SLinus Torvalds /* check queues and dispatch events */
1581da177e4SLinus Torvalds snd_seq_check_queue(q, 1, 0);
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds /* set current tempo */
snd_seq_timer_set_tempo(struct snd_seq_timer * tmr,int tempo)162c7e0b5bfSTakashi Iwai int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds unsigned long flags;
1651da177e4SLinus Torvalds
1667eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
1677eaa943cSTakashi Iwai return -EINVAL;
1681da177e4SLinus Torvalds if (tempo <= 0)
1691da177e4SLinus Torvalds return -EINVAL;
1701da177e4SLinus Torvalds spin_lock_irqsave(&tmr->lock, flags);
1711da177e4SLinus Torvalds if ((unsigned int)tempo != tmr->tempo) {
1721da177e4SLinus Torvalds tmr->tempo = tempo;
173a32f6674SClemens Ladisch snd_seq_timer_set_tick_resolution(tmr);
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
1761da177e4SLinus Torvalds return 0;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds
179671ec859STakashi Iwai /* set current tempo and ppq in a shot */
snd_seq_timer_set_tempo_ppq(struct snd_seq_timer * tmr,int tempo,int ppq)180671ec859STakashi Iwai int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq)
1811da177e4SLinus Torvalds {
182671ec859STakashi Iwai int changed;
1831da177e4SLinus Torvalds unsigned long flags;
1841da177e4SLinus Torvalds
1857eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
1867eaa943cSTakashi Iwai return -EINVAL;
187671ec859STakashi Iwai if (tempo <= 0 || ppq <= 0)
1881da177e4SLinus Torvalds return -EINVAL;
1891da177e4SLinus Torvalds spin_lock_irqsave(&tmr->lock, flags);
1901da177e4SLinus Torvalds if (tmr->running && (ppq != tmr->ppq)) {
1911da177e4SLinus Torvalds /* refuse to change ppq on running timers */
1921da177e4SLinus Torvalds /* because it will upset the song position (ticks) */
1931da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
19404cc79a0STakashi Iwai pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
1951da177e4SLinus Torvalds return -EBUSY;
1961da177e4SLinus Torvalds }
197671ec859STakashi Iwai changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
198671ec859STakashi Iwai tmr->tempo = tempo;
1991da177e4SLinus Torvalds tmr->ppq = ppq;
200671ec859STakashi Iwai if (changed)
201a32f6674SClemens Ladisch snd_seq_timer_set_tick_resolution(tmr);
2021da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
2031da177e4SLinus Torvalds return 0;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds
2061da177e4SLinus Torvalds /* set current tick position */
snd_seq_timer_set_position_tick(struct snd_seq_timer * tmr,snd_seq_tick_time_t position)207c7e0b5bfSTakashi Iwai int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
208c7e0b5bfSTakashi Iwai snd_seq_tick_time_t position)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds unsigned long flags;
2111da177e4SLinus Torvalds
2127eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
2137eaa943cSTakashi Iwai return -EINVAL;
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds spin_lock_irqsave(&tmr->lock, flags);
2161da177e4SLinus Torvalds tmr->tick.cur_tick = position;
2171da177e4SLinus Torvalds tmr->tick.fraction = 0;
2181da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
2191da177e4SLinus Torvalds return 0;
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds
2221da177e4SLinus Torvalds /* set current real-time position */
snd_seq_timer_set_position_time(struct snd_seq_timer * tmr,snd_seq_real_time_t position)223c7e0b5bfSTakashi Iwai int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
224c7e0b5bfSTakashi Iwai snd_seq_real_time_t position)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds unsigned long flags;
2271da177e4SLinus Torvalds
2287eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
2297eaa943cSTakashi Iwai return -EINVAL;
2301da177e4SLinus Torvalds
2311da177e4SLinus Torvalds snd_seq_sanity_real_time(&position);
2321da177e4SLinus Torvalds spin_lock_irqsave(&tmr->lock, flags);
2331da177e4SLinus Torvalds tmr->cur_time = position;
2341da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
2351da177e4SLinus Torvalds return 0;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds
2381da177e4SLinus Torvalds /* set timer skew */
snd_seq_timer_set_skew(struct snd_seq_timer * tmr,unsigned int skew,unsigned int base)239c7e0b5bfSTakashi Iwai int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
240c7e0b5bfSTakashi Iwai unsigned int base)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds unsigned long flags;
2431da177e4SLinus Torvalds
2447eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
2457eaa943cSTakashi Iwai return -EINVAL;
2461da177e4SLinus Torvalds
2471da177e4SLinus Torvalds /* FIXME */
2481da177e4SLinus Torvalds if (base != SKEW_BASE) {
24904cc79a0STakashi Iwai pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
2501da177e4SLinus Torvalds return -EINVAL;
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds spin_lock_irqsave(&tmr->lock, flags);
2531da177e4SLinus Torvalds tmr->skew = skew;
2541da177e4SLinus Torvalds spin_unlock_irqrestore(&tmr->lock, flags);
2551da177e4SLinus Torvalds return 0;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds
snd_seq_timer_open(struct snd_seq_queue * q)258c7e0b5bfSTakashi Iwai int snd_seq_timer_open(struct snd_seq_queue *q)
2591da177e4SLinus Torvalds {
260c7e0b5bfSTakashi Iwai struct snd_timer_instance *t;
261c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
2621da177e4SLinus Torvalds char str[32];
2631da177e4SLinus Torvalds int err;
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds tmr = q->timer;
2667eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
2677eaa943cSTakashi Iwai return -EINVAL;
2681da177e4SLinus Torvalds if (tmr->timeri)
2691da177e4SLinus Torvalds return -EBUSY;
2701da177e4SLinus Torvalds sprintf(str, "sequencer queue %i", q->queue);
2711da177e4SLinus Torvalds if (tmr->type != SNDRV_SEQ_TIMER_ALSA) /* standard ALSA timer */
2721da177e4SLinus Torvalds return -EINVAL;
2731da177e4SLinus Torvalds if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
2741da177e4SLinus Torvalds tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
2756a34367eSTakashi Iwai t = snd_timer_instance_new(str);
2766a34367eSTakashi Iwai if (!t)
2776a34367eSTakashi Iwai return -ENOMEM;
2786a34367eSTakashi Iwai t->callback = snd_seq_timer_interrupt;
2796a34367eSTakashi Iwai t->callback_data = q;
2806a34367eSTakashi Iwai t->flags |= SNDRV_TIMER_IFLG_AUTO;
2816a34367eSTakashi Iwai err = snd_timer_open(t, &tmr->alsa_id, q->queue);
2821da177e4SLinus Torvalds if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
2831da177e4SLinus Torvalds if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
2841da177e4SLinus Torvalds tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
285c7e0b5bfSTakashi Iwai struct snd_timer_id tid;
2861da177e4SLinus Torvalds memset(&tid, 0, sizeof(tid));
2871da177e4SLinus Torvalds tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
2881da177e4SLinus Torvalds tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
2891da177e4SLinus Torvalds tid.card = -1;
2901da177e4SLinus Torvalds tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
2916a34367eSTakashi Iwai err = snd_timer_open(t, &tid, q->queue);
2921da177e4SLinus Torvalds }
29366efdc71STakashi Iwai }
2941da177e4SLinus Torvalds if (err < 0) {
29504cc79a0STakashi Iwai pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
2966a34367eSTakashi Iwai snd_timer_instance_free(t);
2971da177e4SLinus Torvalds return err;
2981da177e4SLinus Torvalds }
2992cdc7b63STakashi Iwai spin_lock_irq(&tmr->lock);
300*83e197a8STakashi Iwai if (tmr->timeri)
301*83e197a8STakashi Iwai err = -EBUSY;
302*83e197a8STakashi Iwai else
3031da177e4SLinus Torvalds tmr->timeri = t;
3042cdc7b63STakashi Iwai spin_unlock_irq(&tmr->lock);
305*83e197a8STakashi Iwai if (err < 0) {
306*83e197a8STakashi Iwai snd_timer_close(t);
307*83e197a8STakashi Iwai snd_timer_instance_free(t);
308*83e197a8STakashi Iwai return err;
309*83e197a8STakashi Iwai }
3101da177e4SLinus Torvalds return 0;
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds
snd_seq_timer_close(struct snd_seq_queue * q)313c7e0b5bfSTakashi Iwai int snd_seq_timer_close(struct snd_seq_queue *q)
3141da177e4SLinus Torvalds {
315c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
3162cdc7b63STakashi Iwai struct snd_timer_instance *t;
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds tmr = q->timer;
3197eaa943cSTakashi Iwai if (snd_BUG_ON(!tmr))
3207eaa943cSTakashi Iwai return -EINVAL;
3212cdc7b63STakashi Iwai spin_lock_irq(&tmr->lock);
3222cdc7b63STakashi Iwai t = tmr->timeri;
3231da177e4SLinus Torvalds tmr->timeri = NULL;
3242cdc7b63STakashi Iwai spin_unlock_irq(&tmr->lock);
3256a34367eSTakashi Iwai if (t) {
3262cdc7b63STakashi Iwai snd_timer_close(t);
3276a34367eSTakashi Iwai snd_timer_instance_free(t);
3286a34367eSTakashi Iwai }
3291da177e4SLinus Torvalds return 0;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds
seq_timer_stop(struct snd_seq_timer * tmr)3322cdc7b63STakashi Iwai static int seq_timer_stop(struct snd_seq_timer *tmr)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds if (! tmr->timeri)
3351da177e4SLinus Torvalds return -EINVAL;
3361da177e4SLinus Torvalds if (!tmr->running)
3371da177e4SLinus Torvalds return 0;
3381da177e4SLinus Torvalds tmr->running = 0;
3391da177e4SLinus Torvalds snd_timer_pause(tmr->timeri);
3401da177e4SLinus Torvalds return 0;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds
snd_seq_timer_stop(struct snd_seq_timer * tmr)3432cdc7b63STakashi Iwai int snd_seq_timer_stop(struct snd_seq_timer *tmr)
3442cdc7b63STakashi Iwai {
3452cdc7b63STakashi Iwai unsigned long flags;
3462cdc7b63STakashi Iwai int err;
3472cdc7b63STakashi Iwai
3482cdc7b63STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
3492cdc7b63STakashi Iwai err = seq_timer_stop(tmr);
3502cdc7b63STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
3512cdc7b63STakashi Iwai return err;
3522cdc7b63STakashi Iwai }
3532cdc7b63STakashi Iwai
initialize_timer(struct snd_seq_timer * tmr)354c7e0b5bfSTakashi Iwai static int initialize_timer(struct snd_seq_timer *tmr)
3551da177e4SLinus Torvalds {
356c7e0b5bfSTakashi Iwai struct snd_timer *t;
35787ef7779SClemens Ladisch unsigned long freq;
35887ef7779SClemens Ladisch
3591da177e4SLinus Torvalds t = tmr->timeri->timer;
36043a35428STakashi Iwai if (!t)
3617eaa943cSTakashi Iwai return -EINVAL;
3621da177e4SLinus Torvalds
36387ef7779SClemens Ladisch freq = tmr->preferred_resolution;
36487ef7779SClemens Ladisch if (!freq)
36587ef7779SClemens Ladisch freq = DEFAULT_FREQUENCY;
36687ef7779SClemens Ladisch else if (freq < MIN_FREQUENCY)
36787ef7779SClemens Ladisch freq = MIN_FREQUENCY;
36887ef7779SClemens Ladisch else if (freq > MAX_FREQUENCY)
36987ef7779SClemens Ladisch freq = MAX_FREQUENCY;
37087ef7779SClemens Ladisch
3711da177e4SLinus Torvalds tmr->ticks = 1;
37287ef7779SClemens Ladisch if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
37321244e3dSTakashi Iwai unsigned long r = snd_timer_resolution(tmr->timeri);
3741da177e4SLinus Torvalds if (r) {
37587ef7779SClemens Ladisch tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
3761da177e4SLinus Torvalds if (! tmr->ticks)
3771da177e4SLinus Torvalds tmr->ticks = 1;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds tmr->initialized = 1;
3811da177e4SLinus Torvalds return 0;
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds
seq_timer_start(struct snd_seq_timer * tmr)3842cdc7b63STakashi Iwai static int seq_timer_start(struct snd_seq_timer *tmr)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds if (! tmr->timeri)
3871da177e4SLinus Torvalds return -EINVAL;
3881da177e4SLinus Torvalds if (tmr->running)
3892cdc7b63STakashi Iwai seq_timer_stop(tmr);
3902cdc7b63STakashi Iwai seq_timer_reset(tmr);
3911da177e4SLinus Torvalds if (initialize_timer(tmr) < 0)
3921da177e4SLinus Torvalds return -EINVAL;
3931da177e4SLinus Torvalds snd_timer_start(tmr->timeri, tmr->ticks);
3941da177e4SLinus Torvalds tmr->running = 1;
3953915bf29SArnd Bergmann ktime_get_ts64(&tmr->last_update);
3961da177e4SLinus Torvalds return 0;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds
snd_seq_timer_start(struct snd_seq_timer * tmr)3992cdc7b63STakashi Iwai int snd_seq_timer_start(struct snd_seq_timer *tmr)
4002cdc7b63STakashi Iwai {
4012cdc7b63STakashi Iwai unsigned long flags;
4022cdc7b63STakashi Iwai int err;
4032cdc7b63STakashi Iwai
4042cdc7b63STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
4052cdc7b63STakashi Iwai err = seq_timer_start(tmr);
4062cdc7b63STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
4072cdc7b63STakashi Iwai return err;
4082cdc7b63STakashi Iwai }
4092cdc7b63STakashi Iwai
seq_timer_continue(struct snd_seq_timer * tmr)4102cdc7b63STakashi Iwai static int seq_timer_continue(struct snd_seq_timer *tmr)
4112cdc7b63STakashi Iwai {
4122cdc7b63STakashi Iwai if (! tmr->timeri)
4132cdc7b63STakashi Iwai return -EINVAL;
4142cdc7b63STakashi Iwai if (tmr->running)
4152cdc7b63STakashi Iwai return -EBUSY;
4162cdc7b63STakashi Iwai if (! tmr->initialized) {
4172cdc7b63STakashi Iwai seq_timer_reset(tmr);
4182cdc7b63STakashi Iwai if (initialize_timer(tmr) < 0)
4192cdc7b63STakashi Iwai return -EINVAL;
4202cdc7b63STakashi Iwai }
4212cdc7b63STakashi Iwai snd_timer_start(tmr->timeri, tmr->ticks);
4222cdc7b63STakashi Iwai tmr->running = 1;
4233915bf29SArnd Bergmann ktime_get_ts64(&tmr->last_update);
4242cdc7b63STakashi Iwai return 0;
4252cdc7b63STakashi Iwai }
4262cdc7b63STakashi Iwai
snd_seq_timer_continue(struct snd_seq_timer * tmr)427c7e0b5bfSTakashi Iwai int snd_seq_timer_continue(struct snd_seq_timer *tmr)
4281da177e4SLinus Torvalds {
4292cdc7b63STakashi Iwai unsigned long flags;
4302cdc7b63STakashi Iwai int err;
4312cdc7b63STakashi Iwai
4322cdc7b63STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
4332cdc7b63STakashi Iwai err = seq_timer_continue(tmr);
4342cdc7b63STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
4352cdc7b63STakashi Iwai return err;
4361da177e4SLinus Torvalds }
4371da177e4SLinus Torvalds
4381da177e4SLinus 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)439dc749779STakashi Iwai snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
440dc749779STakashi Iwai bool adjust_ktime)
4411da177e4SLinus Torvalds {
4421da177e4SLinus Torvalds snd_seq_real_time_t cur_time;
4432cdc7b63STakashi Iwai unsigned long flags;
4441da177e4SLinus Torvalds
4452cdc7b63STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
4461da177e4SLinus Torvalds cur_time = tmr->cur_time;
447dc749779STakashi Iwai if (adjust_ktime && tmr->running) {
4483915bf29SArnd Bergmann struct timespec64 tm;
4493915bf29SArnd Bergmann
4503915bf29SArnd Bergmann ktime_get_ts64(&tm);
4513915bf29SArnd Bergmann tm = timespec64_sub(tm, tmr->last_update);
4529b50898aSTakashi Iwai cur_time.tv_nsec += tm.tv_nsec;
4539b50898aSTakashi Iwai cur_time.tv_sec += tm.tv_sec;
4541da177e4SLinus Torvalds snd_seq_sanity_real_time(&cur_time);
4551da177e4SLinus Torvalds }
4562cdc7b63STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
4571da177e4SLinus Torvalds return cur_time;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds
4601da177e4SLinus Torvalds /* TODO: use interpolation on tick queue (will only be useful for very
4611da177e4SLinus Torvalds high PPQ values) */
snd_seq_timer_get_cur_tick(struct snd_seq_timer * tmr)462c7e0b5bfSTakashi Iwai snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
4631da177e4SLinus Torvalds {
464dc749779STakashi Iwai snd_seq_tick_time_t cur_tick;
465dc749779STakashi Iwai unsigned long flags;
466dc749779STakashi Iwai
467dc749779STakashi Iwai spin_lock_irqsave(&tmr->lock, flags);
468dc749779STakashi Iwai cur_tick = tmr->tick.cur_tick;
469dc749779STakashi Iwai spin_unlock_irqrestore(&tmr->lock, flags);
470dc749779STakashi Iwai return cur_tick;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds
4731da177e4SLinus Torvalds
474cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
4751da177e4SLinus Torvalds /* exported to seq_info.c */
snd_seq_info_timer_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)476c7e0b5bfSTakashi Iwai void snd_seq_info_timer_read(struct snd_info_entry *entry,
477c7e0b5bfSTakashi Iwai struct snd_info_buffer *buffer)
4781da177e4SLinus Torvalds {
4791da177e4SLinus Torvalds int idx;
480c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
481c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
482c7e0b5bfSTakashi Iwai struct snd_timer_instance *ti;
4831da177e4SLinus Torvalds unsigned long resolution;
4841da177e4SLinus Torvalds
4851da177e4SLinus Torvalds for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
4861da177e4SLinus Torvalds q = queueptr(idx);
4871da177e4SLinus Torvalds if (q == NULL)
4881da177e4SLinus Torvalds continue;
48960adcfdeSTakashi Iwai mutex_lock(&q->timer_mutex);
49060adcfdeSTakashi Iwai tmr = q->timer;
49160adcfdeSTakashi Iwai if (!tmr)
49260adcfdeSTakashi Iwai goto unlock;
49360adcfdeSTakashi Iwai ti = tmr->timeri;
49460adcfdeSTakashi Iwai if (!ti)
49560adcfdeSTakashi Iwai goto unlock;
4961da177e4SLinus Torvalds snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
4971da177e4SLinus Torvalds resolution = snd_timer_resolution(ti) * tmr->ticks;
4981da177e4SLinus Torvalds snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
4991da177e4SLinus Torvalds snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
50060adcfdeSTakashi Iwai unlock:
50160adcfdeSTakashi Iwai mutex_unlock(&q->timer_mutex);
5021da177e4SLinus Torvalds queuefree(q);
5031da177e4SLinus Torvalds }
5041da177e4SLinus Torvalds }
505cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */
50604f141a8STakashi Iwai
507