aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/asound.h1
-rw-r--r--sound/core/Kconfig21
-rw-r--r--sound/core/Makefile2
-rw-r--r--sound/core/hrtimer.c140
-rw-r--r--sound/core/seq/seq.c4
5 files changed, 167 insertions, 1 deletions
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 2c4dc908a54..1c02ed1d7c4 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -575,6 +575,7 @@ enum {
#define SNDRV_TIMER_GLOBAL_SYSTEM 0
#define SNDRV_TIMER_GLOBAL_RTC 1
#define SNDRV_TIMER_GLOBAL_HPET 2
+#define SNDRV_TIMER_GLOBAL_HRTIMER 3
/* info flags */
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 66348c92f88..406a4501098 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -95,6 +95,26 @@ config SND_SEQUENCER_OSS
this will be compiled as a module. The module will be called
snd-seq-oss.
+config SND_HRTIMER
+ tristate "HR-timer backend support"
+ depends on HIGH_RES_TIMERS
+ select SND_TIMER
+ help
+ Say Y here to enable HR-timer backend for ALSA timer. ALSA uses
+ the hrtimer as a precise timing source. The ALSA sequencer code
+ also can use this timing source.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hrtimer.
+
+config SND_SEQ_HRTIMER_DEFAULT
+ bool "Use HR-timer as default sequencer timer"
+ depends on SND_HRTIMER && SND_SEQUENCER
+ default y
+ help
+ Say Y here to use the HR-timer backend as the default sequencer
+ timer.
+
config SND_RTCTIMER
tristate "RTC Timer support"
depends on RTC
@@ -114,6 +134,7 @@ config SND_RTCTIMER
config SND_SEQ_RTCTIMER_DEFAULT
bool "Use RTC as default sequencer timer"
depends on SND_RTCTIMER && SND_SEQUENCER
+ dpeends on !SND_SEQ_HRTIMER_DEFAULT
default y
help
Say Y here to use the RTC timer as the default sequencer
diff --git a/sound/core/Makefile b/sound/core/Makefile
index d57125a5687..4229052e7b9 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -17,12 +17,14 @@ snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
+snd-hrtimer-objs := hrtimer.o
snd-rtctimer-objs := rtctimer.o
snd-hwdep-objs := hwdep.o
obj-$(CONFIG_SND) += snd.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o
+obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
new file mode 100644
index 00000000000..b712d7f211f
--- /dev/null
+++ b/sound/core/hrtimer.c
@@ -0,0 +1,140 @@
+/*
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/hrtimer.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA hrtimer backend");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER));
+
+#define NANO_SEC 1000000000UL /* 10^9 in sec */
+static unsigned int resolution;
+
+struct snd_hrtimer {
+ struct snd_timer *timer;
+ struct hrtimer hrt;
+};
+
+static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
+{
+ struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
+ struct snd_timer *t = stime->timer;
+ hrtimer_forward_now(hrt, ktime_set(0, t->sticks * resolution));
+ snd_timer_interrupt(stime->timer, t->sticks);
+ return HRTIMER_RESTART;
+}
+
+static int snd_hrtimer_open(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime;
+
+ stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+ if (!stime)
+ return -ENOMEM;
+ hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ stime->timer = t;
+ stime->hrt.cb_mode = HRTIMER_CB_SOFTIRQ;
+ stime->hrt.function = snd_hrtimer_callback;
+ t->private_data = stime;
+ return 0;
+}
+
+static int snd_hrtimer_close(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ if (stime) {
+ hrtimer_cancel(&stime->hrt);
+ kfree(stime);
+ t->private_data = NULL;
+ }
+ return 0;
+}
+
+static int snd_hrtimer_start(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ hrtimer_start(&stime->hrt, ktime_set(0, t->sticks * resolution),
+ HRTIMER_MODE_REL);
+ return 0;
+}
+
+static int snd_hrtimer_stop(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ hrtimer_cancel(&stime->hrt);
+ return 0;
+}
+
+static struct snd_timer_hardware hrtimer_hw = {
+ .flags = (SNDRV_TIMER_HW_AUTO |
+ /*SNDRV_TIMER_HW_FIRST |*/
+ SNDRV_TIMER_HW_TASKLET),
+ .open = snd_hrtimer_open,
+ .close = snd_hrtimer_close,
+ .start = snd_hrtimer_start,
+ .stop = snd_hrtimer_stop,
+};
+
+/*
+ * entry functions
+ */
+
+static struct snd_timer *mytimer;
+
+static int __init snd_hrtimer_init(void)
+{
+ struct snd_timer *timer;
+ struct timespec tp;
+ int err;
+
+ hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ if (tp.tv_sec > 0 || !tp.tv_nsec) {
+ snd_printk(KERN_ERR
+ "snd-hrtimer: Invalid resolution %u.%09u",
+ (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
+ return -EINVAL;
+ }
+ resolution = tp.tv_nsec;
+
+ /* Create a new timer and set up the fields */
+ err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
+ &timer);
+ if (err < 0)
+ return err;
+
+ timer->module = THIS_MODULE;
+ strcpy(timer->name, "HR timer");
+ timer->hw = hrtimer_hw;
+ timer->hw.resolution = resolution;
+ timer->hw.ticks = NANO_SEC / resolution;
+
+ err = snd_timer_global_register(timer);
+ if (err < 0) {
+ snd_timer_global_free(timer);
+ return err;
+ }
+ mytimer = timer; /* remember this */
+
+ return 0;
+}
+
+static void __exit snd_hrtimer_exit(void)
+{
+ if (mytimer) {
+ snd_timer_global_free(mytimer);
+ mytimer = NULL;
+ }
+}
+
+module_init(snd_hrtimer_init);
+module_exit(snd_hrtimer_exit);
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index ee0f8405ab3..bf09a5ad186 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -43,7 +43,9 @@ int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
int seq_default_timer_card = -1;
int seq_default_timer_device =
-#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT
+#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
+ SNDRV_TIMER_GLOBAL_HRTIMER
+#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
SNDRV_TIMER_GLOBAL_RTC
#else
SNDRV_TIMER_GLOBAL_SYSTEM