File: /usr/src/linux/drivers/sound/emu10k1/timer.c

1     
2     /*
3      **********************************************************************
4      *     timer.c
5      *     Copyright (C) 1999, 2000 Creative Labs, inc.
6      *
7      **********************************************************************
8      *
9      *     This program is free software; you can redistribute it and/or
10      *     modify it under the terms of the GNU General Public License as
11      *     published by the Free Software Foundation; either version 2 of
12      *     the License, or (at your option) any later version.
13      *
14      *     This program is distributed in the hope that it will be useful,
15      *     but WITHOUT ANY WARRANTY; without even the implied warranty of
16      *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17      *     GNU General Public License for more details.
18      *
19      *     You should have received a copy of the GNU General Public
20      *     License along with this program; if not, write to the Free
21      *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
22      *     USA.
23      *
24      **********************************************************************
25      */
26     
27     /* 3/6/2000	Improved support for different timer delays  Rui Sousa */
28     
29     /* 4/3/2000	Implemented timer list using list.h 	     Rui Sousa */
30     
31     #include "hwaccess.h"
32     #include "8010.h"
33     #include "irqmgr.h"
34     #include "timer.h"
35     
36     /* Try to schedule only once per fragment */
37     
38     void emu10k1_timer_irqhandler(struct emu10k1_card *card)
39     {
40     	struct emu_timer *t;
41     	struct list_head *entry;
42     
43     	spin_lock(&card->timer_lock);
44     
45     	list_for_each(entry, &card->timers) {
46     		t = list_entry(entry, struct emu_timer, list);
47     
48     		if (t->state & TIMER_STATE_ACTIVE) {
49     			t->count++;
50     			if (t->count == t->count_max) {
51     				t->count = 0;
52     				tasklet_hi_schedule(&t->tasklet);
53     			}
54     		}
55     	}
56     
57     	spin_unlock(&card->timer_lock);
58     
59     	return;
60     }
61     
62     void emu10k1_timer_install(struct emu10k1_card *card, struct emu_timer *timer, u32 delay)
63     {
64     	struct emu_timer *t;
65     	struct list_head *entry;
66     	unsigned long flags;
67     
68     	if (delay < 5)
69     		delay = 5;
70     
71     	timer->delay = delay;
72     	timer->state = TIMER_STATE_INSTALLED;
73     
74     	spin_lock_irqsave(&card->timer_lock, flags);
75     
76     	timer->count_max = timer->delay / (card->timer_delay < 1024 ? card->timer_delay : 1024);
77     	timer->count = timer->count_max - 1;
78     
79     	list_add(&timer->list, &card->timers);
80     
81     	if (card->timer_delay > delay) {
82     		if (card->timer_delay == TIMER_STOPPED)
83     			emu10k1_irq_enable(card, INTE_INTERVALTIMERENB);
84     
85     		card->timer_delay = delay;
86     		delay = (delay < 1024 ? delay : 1024);
87     
88     		emu10k1_writefn0(card, TIMER_RATE, delay);
89     
90     		list_for_each(entry, &card->timers) {
91     			t = list_entry(entry, struct emu_timer, list);
92     
93     			t->count_max = t->delay / delay;
94     			/* don't want to think much, just force scheduling 
95     			   on the next interrupt */
96     			t->count = t->count_max - 1;
97     		}
98     
99     		DPD(2, "timer rate --> %u\n", delay);
100     	}
101     
102     	spin_unlock_irqrestore(&card->timer_lock, flags);
103     
104     	return;
105     }
106     
107     void emu10k1_timer_uninstall(struct emu10k1_card *card, struct emu_timer *timer)
108     {
109     	struct emu_timer *t;
110     	struct list_head *entry;
111     	u32 delay = TIMER_STOPPED;
112     	unsigned long flags;
113     
114     	if (timer->state == TIMER_STATE_UNINSTALLED)
115     		return;
116     
117     	spin_lock_irqsave(&card->timer_lock, flags);
118     
119     	list_del(&timer->list);
120     
121     	list_for_each(entry, &card->timers) {
122     		t = list_entry(entry, struct emu_timer, list);
123     
124     		if (t->delay < delay)
125     			delay = t->delay;
126     	}
127     
128     	if (card->timer_delay != delay) {
129     		card->timer_delay = delay;
130     
131     		if (delay == TIMER_STOPPED)
132     			emu10k1_irq_disable(card, INTE_INTERVALTIMERENB);
133     		else {
134     			delay = (delay < 1024 ? delay : 1024);
135     
136     			emu10k1_writefn0(card, TIMER_RATE, delay);
137     
138     			list_for_each(entry, &card->timers) {
139     				t = list_entry(entry, struct emu_timer, list);
140     
141     				t->count_max = t->delay / delay;
142     				t->count = t->count_max - 1;
143     			}
144     		}
145     
146     		DPD(2, "timer rate --> %u\n", delay);
147     	}
148     
149     	spin_unlock_irqrestore(&card->timer_lock, flags);
150     
151     	timer->state = TIMER_STATE_UNINSTALLED;
152     
153     	return;
154     }
155     
156     void emu10k1_timer_enable(struct emu10k1_card *card, struct emu_timer *timer)
157     {
158     	unsigned long flags;
159     
160     	spin_lock_irqsave(&card->timer_lock, flags);
161     	timer->state |= TIMER_STATE_ACTIVE;
162     	spin_unlock_irqrestore(&card->timer_lock, flags);
163     
164     	return;
165     }
166     
167     void emu10k1_timer_disable(struct emu10k1_card *card, struct emu_timer *timer)
168     {
169     	unsigned long flags;
170     
171     	spin_lock_irqsave(&card->timer_lock, flags);
172     	timer->state &= ~TIMER_STATE_ACTIVE;
173     	spin_unlock_irqrestore(&card->timer_lock, flags);
174     
175     	return;
176     }
177