| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  RTC based high-frequency timer | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2000 Takashi Iwai | 
					
						
							|  |  |  |  *	based on rtctimer.c by Steve Ratcliffe | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  *   it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  *   the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  *   (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  *   GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  *   along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/moduleparam.h>
 | 
					
						
							| 
									
										
										
										
											2007-02-22 13:23:01 +01:00
										 |  |  | #include <linux/log2.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <sound/core.h>
 | 
					
						
							|  |  |  | #include <sound/timer.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/mc146818rtc.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define RTC_FREQ	1024		/* default frequency */
 | 
					
						
							|  |  |  | #define NANO_SEC	1000000000L	/* 10^9 in sec */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * prototypes | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | static int rtctimer_open(struct snd_timer *t); | 
					
						
							|  |  |  | static int rtctimer_close(struct snd_timer *t); | 
					
						
							|  |  |  | static int rtctimer_start(struct snd_timer *t); | 
					
						
							|  |  |  | static int rtctimer_stop(struct snd_timer *t); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * The hardware dependent description for this timer. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | static struct snd_timer_hardware rtc_hw = { | 
					
						
							| 
									
										
										
										
											2006-10-27 10:45:00 +02:00
										 |  |  | 	.flags =	SNDRV_TIMER_HW_AUTO | | 
					
						
							|  |  |  | 			SNDRV_TIMER_HW_FIRST | | 
					
						
							|  |  |  | 			SNDRV_TIMER_HW_TASKLET, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	.ticks =	100000000L,		/* FIXME: XXX */ | 
					
						
							|  |  |  | 	.open =		rtctimer_open, | 
					
						
							|  |  |  | 	.close =	rtctimer_close, | 
					
						
							|  |  |  | 	.start =	rtctimer_start, | 
					
						
							|  |  |  | 	.stop =		rtctimer_stop, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rtctimer_freq = RTC_FREQ;		/* frequency */ | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | static struct snd_timer *rtctimer; | 
					
						
							| 
									
										
										
										
											2006-10-27 10:45:00 +02:00
										 |  |  | static struct tasklet_struct rtc_tasklet; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static rtc_task_t rtc_task; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | rtctimer_open(struct snd_timer *t) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = rtc_register(&rtc_task); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	t->private_data = &rtc_task; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | rtctimer_close(struct snd_timer *t) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	rtc_task_t *rtc = t->private_data; | 
					
						
							|  |  |  | 	if (rtc) { | 
					
						
							|  |  |  | 		rtc_unregister(rtc); | 
					
						
							| 
									
										
										
										
											2006-10-27 10:45:00 +02:00
										 |  |  | 		tasklet_kill(&rtc_tasklet); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		t->private_data = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | rtctimer_start(struct snd_timer *timer) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	rtc_task_t *rtc = timer->private_data; | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!rtc)) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); | 
					
						
							|  |  |  | 	rtc_control(rtc, RTC_PIE_ON, 0); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | rtctimer_stop(struct snd_timer *timer) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	rtc_task_t *rtc = timer->private_data; | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!rtc)) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rtc_control(rtc, RTC_PIE_OFF, 0); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-27 10:45:00 +02:00
										 |  |  | static void rtctimer_tasklet(unsigned long data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_timer_interrupt((struct snd_timer *)data, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * interrupt | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void rtctimer_interrupt(void *private_data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-18 12:17:55 +01:00
										 |  |  | 	tasklet_schedule(private_data); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  ENTRY functions | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int __init rtctimer_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-09-27 15:57:24 +02:00
										 |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2005-11-17 13:56:05 +01:00
										 |  |  | 	struct snd_timer *timer; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-27 15:57:24 +02:00
										 |  |  | 	if (rtctimer_freq < 2 || rtctimer_freq > 8192 || | 
					
						
							| 
									
										
										
										
											2007-02-22 13:23:01 +01:00
										 |  |  | 	    !is_power_of_2(rtctimer_freq)) { | 
					
						
							| 
									
										
										
										
											2005-10-12 17:12:31 +02:00
										 |  |  | 		snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", | 
					
						
							|  |  |  | 			   rtctimer_freq); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Create a new timer and set up the fields */ | 
					
						
							|  |  |  | 	err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-12 17:12:31 +02:00
										 |  |  | 	timer->module = THIS_MODULE; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	strcpy(timer->name, "RTC timer"); | 
					
						
							|  |  |  | 	timer->hw = rtc_hw; | 
					
						
							|  |  |  | 	timer->hw.resolution = NANO_SEC / rtctimer_freq; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-27 10:45:00 +02:00
										 |  |  | 	tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/* set up RTC callback */ | 
					
						
							|  |  |  | 	rtc_task.func = rtctimer_interrupt; | 
					
						
							| 
									
										
										
										
											2006-10-27 10:45:00 +02:00
										 |  |  | 	rtc_task.private_data = &rtc_tasklet; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = snd_timer_global_register(timer); | 
					
						
							|  |  |  | 	if (err < 0) { | 
					
						
							|  |  |  | 		snd_timer_global_free(timer); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rtctimer = timer; /* remember this */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit rtctimer_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (rtctimer) { | 
					
						
							| 
									
										
										
										
											2006-06-23 14:38:23 +02:00
										 |  |  | 		snd_timer_global_free(rtctimer); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		rtctimer = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * exported stuff | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | module_init(rtctimer_init) | 
					
						
							|  |  |  | module_exit(rtctimer_exit) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param(rtctimer_freq, int, 0444); | 
					
						
							|  |  |  | MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
 |