Index: alsa-kernel/core/timer.c
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/core/timer.c,v
retrieving revision 1.67
diff -u -r1.67 timer.c
--- alsa-kernel/core/timer.c	12 Oct 2005 16:36:55 -0000	1.67
+++ alsa-kernel/core/timer.c	31 Oct 2005 17:22:04 -0000
@@ -424,6 +424,9 @@
 static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri,
 			    unsigned long sticks)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&timer->lock, flags);
 	list_del(&timeri->active_list);
 	list_add_tail(&timeri->active_list, &timer->active_list_head);
 	if (timer->running) {
@@ -431,13 +434,19 @@
 			goto __start_now;
 		timer->flags |= SNDRV_TIMER_FLG_RESCHED;
 		timeri->flags |= SNDRV_TIMER_IFLG_START;
+		spin_unlock_irqrestore(&timer->lock, flags);
 		return 1;	/* delayed start */
 	} else {
 		timer->sticks = sticks;
+		if (!(timer->hw.flags & SNDRV_TIMER_HW_ATOMIC))
+			spin_unlock_irqrestore(&timer->lock, flags);
 		timer->hw.start(timer);
+		if (!(timer->hw.flags & SNDRV_TIMER_HW_ATOMIC))
+			spin_lock_irqsave(&timer->lock, flags);
 	      __start_now:
 		timer->running++;
 		timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
+		spin_unlock_irqrestore(&timer->lock, flags);
 		return 0;
 	}
 }
@@ -477,8 +486,8 @@
 	spin_lock_irqsave(&timer->lock, flags);
 	timeri->ticks = timeri->cticks = ticks;
 	timeri->pticks = 0;
-	result = snd_timer_start1(timer, timeri, ticks);
 	spin_unlock_irqrestore(&timer->lock, flags);
+	result = snd_timer_start1(timer, timeri, ticks);
 	snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
 	return result;
 }
@@ -488,6 +497,7 @@
 {
 	snd_timer_t *timer;
 	unsigned long flags;
+	int running = 1;
 
 	snd_assert(timeri != NULL, return -ENXIO);
 
@@ -505,22 +515,32 @@
 	spin_lock_irqsave(&timer->lock, flags);
 	list_del_init(&timeri->ack_list);
 	list_del_init(&timeri->active_list);
-	if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
-	    !(--timer->running)) {
+	if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING)
+		running = --timer->running;
+	spin_unlock_irqrestore(&timer->lock, flags);
+	if (! running) {
 		timer->hw.stop(timer);
+		spin_lock_irqsave(&timer->lock, flags);
 		if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
 			timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
 			snd_timer_reschedule(timer, 0);
 			if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
 				timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
+				if (! (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC))
+					spin_unlock_irqrestore(&timer->lock, flags);
 				timer->hw.start(timer);
+				if (! (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC))
+					spin_lock_irqsave(&timer->lock, flags);
 			}
 		}
+		spin_unlock_irqrestore(&timer->lock, flags);
 	}
-	if (!keep_flag)
+	if (!keep_flag) {
+		spin_lock_irqsave(&slave_active_lock, flags);
 		timeri->flags &=
 			~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
-	spin_unlock_irqrestore(&timer->lock, flags);
+		spin_unlock_irqrestore(&slave_active_lock, flags);
+	}
       __end:
 	if (event != SNDRV_TIMER_EVENT_RESOLUTION)
 		snd_timer_notify1(timeri, event);
@@ -569,8 +589,8 @@
 	if (!timeri->cticks)
 		timeri->cticks = 1;
 	timeri->pticks = 0;
-	result = snd_timer_start1(timer, timeri, timer->sticks);
 	spin_unlock_irqrestore(&timer->lock, flags);
+	result = snd_timer_start1(timer, timeri, timer->sticks);
 	snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
 	return result;
 }
@@ -718,17 +738,19 @@
 	if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
 		snd_timer_reschedule(timer, ticks_left);
 	if (timer->running) {
-		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
+		if ((timer->hw.flags & SNDRV_TIMER_HW_STOP) &&
+		    (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)) {
 			timer->hw.stop(timer);
 			timer->flags |= SNDRV_TIMER_FLG_CHANGE;
 		}
-		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
-		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
+		if ((!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
+		     (timer->flags & SNDRV_TIMER_FLG_CHANGE)) &&
+		    (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC)){
 			/* restart timer */
 			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
 			timer->hw.start(timer);
 		}
-	} else {
+	} else if (timer->hw.flags & SNDRV_TIMER_HW_ATOMIC) {
 		timer->hw.stop(timer);
 	}
 
@@ -1015,7 +1037,7 @@
 
 static struct _snd_timer_hardware snd_timer_system =
 {
-	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
+	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET | SNDRV_TIMER_HW_ATOMIC,
 	.resolution =	1000000000L / HZ,
 	.ticks =	10000000L,
 	.start =	snd_timer_s_start,
Index: alsa-kernel/include/timer.h
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/include/timer.h,v
retrieving revision 1.14
diff -u -r1.14 timer.h
--- alsa-kernel/include/timer.h	12 Oct 2005 16:36:55 -0000	1.14
+++ alsa-kernel/include/timer.h	31 Oct 2005 17:22:04 -0000
@@ -51,6 +51,7 @@
 #define SNDRV_TIMER_HW_SLAVE	0x00000004	/* only slave timer (variable resolution) */
 #define SNDRV_TIMER_HW_FIRST	0x00000008	/* first tick can be incomplete */
 #define SNDRV_TIMER_HW_TASKLET	0x00000010	/* timer is called from tasklet */
+#define SNDRV_TIMER_HW_ATOMIC	0x00000020	/* start/stop are atomoic */
 
 #define SNDRV_TIMER_IFLG_SLAVE	  0x00000001
 #define SNDRV_TIMER_IFLG_RUNNING  0x00000002
