aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/ctxfi/cttimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/ctxfi/cttimer.c')
-rw-r--r--sound/pci/ctxfi/cttimer.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
new file mode 100644
index 000000000000..3acb26d0c4cc
--- /dev/null
+++ b/sound/pci/ctxfi/cttimer.c
@@ -0,0 +1,417 @@
1/*
2 * PCM timer handling on ctxfi
3 *
4 * This source file is released under GPL v2 license (no other versions).
5 * See the COPYING file included in the main directory of this source
6 * distribution for the license terms and conditions.
7 */
8
9#include <linux/slab.h>
10#include <sound/core.h>
11#include <sound/pcm.h>
12#include "ctatc.h"
13#include "cthardware.h"
14#include "cttimer.h"
15
16struct ct_timer_ops {
17 void (*init)(struct ct_timer_instance *);
18 void (*prepare)(struct ct_timer_instance *);
19 void (*start)(struct ct_timer_instance *);
20 void (*stop)(struct ct_timer_instance *);
21 void (*free_instance)(struct ct_timer_instance *);
22 void (*interrupt)(struct ct_timer *);
23 void (*free_global)(struct ct_timer *);
24};
25
26/* timer instance -- assigned to each PCM stream */
27struct ct_timer_instance {
28 spinlock_t lock;
29 struct ct_timer *timer_base;
30 struct ct_atc_pcm *apcm;
31 struct snd_pcm_substream *substream;
32 struct timer_list timer;
33 struct list_head instance_list;
34 struct list_head running_list;
35 unsigned int position;
36 unsigned int frag_count;
37 unsigned int running:1;
38 unsigned int need_update:1;
39};
40
41/* timer instance manager */
42struct ct_timer {
43 spinlock_t lock; /* global timer lock (for xfitimer) */
44 spinlock_t list_lock; /* lock for instance list */
45 struct ct_atc *atc;
46 struct ct_timer_ops *ops;
47 struct list_head instance_head;
48 struct list_head running_head;
49 unsigned int irq_handling:1; /* in IRQ handling */
50 unsigned int reprogram:1; /* need to reprogram the internval */
51 unsigned int running:1; /* global timer running */
52};
53
54
55/*
56 * system-timer-based updates
57 */
58
59static void ct_systimer_callback(unsigned long data)
60{
61 struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
62 struct snd_pcm_substream *substream = ti->substream;
63 struct snd_pcm_runtime *runtime = substream->runtime;
64 struct ct_atc_pcm *apcm = ti->apcm;
65 unsigned int period_size = runtime->period_size;
66 unsigned int buffer_size = runtime->buffer_size;
67 unsigned long flags;
68 unsigned int position, dist, interval;
69
70 position = substream->ops->pointer(substream);
71 dist = (position + buffer_size - ti->position) % buffer_size;
72 if (dist >= period_size ||
73 position / period_size != ti->position / period_size) {
74 apcm->interrupt(apcm);
75 ti->position = position;
76 }
77 /* Add extra HZ*5/1000 to avoid overrun issue when recording
78 * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
79 interval = ((period_size - (position % period_size))
80 * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
81 spin_lock_irqsave(&ti->lock, flags);
82 if (ti->running)
83 mod_timer(&ti->timer, jiffies + interval);
84 spin_unlock_irqrestore(&ti->lock, flags);
85}
86
87static void ct_systimer_init(struct ct_timer_instance *ti)
88{
89 setup_timer(&ti->timer, ct_systimer_callback,
90 (unsigned long)ti);
91}
92
93static void ct_systimer_start(struct ct_timer_instance *ti)
94{
95 struct snd_pcm_runtime *runtime = ti->substream->runtime;
96 unsigned long flags;
97
98 spin_lock_irqsave(&ti->lock, flags);
99 ti->running = 1;
100 mod_timer(&ti->timer,
101 jiffies + (runtime->period_size * HZ +
102 (runtime->rate - 1)) / runtime->rate);
103 spin_unlock_irqrestore(&ti->lock, flags);
104}
105
106static void ct_systimer_stop(struct ct_timer_instance *ti)
107{
108 unsigned long flags;
109
110 spin_lock_irqsave(&ti->lock, flags);
111 ti->running = 0;
112 del_timer(&ti->timer);
113 spin_unlock_irqrestore(&ti->lock, flags);
114}
115
116static void ct_systimer_prepare(struct ct_timer_instance *ti)
117{
118 ct_systimer_stop(ti);
119 try_to_del_timer_sync(&ti->timer);
120}
121
122#define ct_systimer_free ct_systimer_prepare
123
124static struct ct_timer_ops ct_systimer_ops = {
125 .init = ct_systimer_init,
126 .free_instance = ct_systimer_free,
127 .prepare = ct_systimer_prepare,
128 .start = ct_systimer_start,
129 .stop = ct_systimer_stop,
130};
131
132
133/*
134 * Handling multiple streams using a global emu20k1 timer irq
135 */
136
137#define CT_TIMER_FREQ 48000
138#define MAX_TICKS ((1 << 13) - 1)
139
140static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
141{
142 struct hw *hw = atimer->atc->hw;
143 if (ticks > MAX_TICKS)
144 ticks = MAX_TICKS;
145 hw->set_timer_tick(hw, ticks);
146 if (!atimer->running)
147 hw->set_timer_irq(hw, 1);
148 atimer->running = 1;
149}
150
151static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
152{
153 if (atimer->running) {
154 struct hw *hw = atimer->atc->hw;
155 hw->set_timer_irq(hw, 0);
156 hw->set_timer_tick(hw, 0);
157 atimer->running = 0;
158 }
159}
160
161/*
162 * reprogram the timer interval;
163 * checks the running instance list and determines the next timer interval.
164 * also updates the each stream position, returns the number of streams
165 * to call snd_pcm_period_elapsed() appropriately
166 *
167 * call this inside the lock and irq disabled
168 */
169static int ct_xfitimer_reprogram(struct ct_timer *atimer)
170{
171 struct ct_timer_instance *ti;
172 int min_intr = -1;
173 int updates = 0;
174
175 list_for_each_entry(ti, &atimer->running_head, running_list) {
176 struct snd_pcm_runtime *runtime;
177 unsigned int pos, diff;
178 int intr;
179 runtime = ti->substream->runtime;
180 pos = ti->substream->ops->pointer(ti->substream);
181 if (pos < ti->position)
182 diff = runtime->buffer_size - ti->position + pos;
183 else
184 diff = pos - ti->position;
185 ti->position = pos;
186 while (diff >= ti->frag_count) {
187 ti->frag_count += runtime->period_size;
188 ti->need_update = 1;
189 updates++;
190 }
191 ti->frag_count -= diff;
192 intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ,
193 runtime->rate);
194 if (min_intr < 0 || intr < min_intr)
195 min_intr = intr;
196 }
197
198 if (min_intr > 0)
199 ct_xfitimer_irq_rearm(atimer, min_intr);
200 else
201 ct_xfitimer_irq_stop(atimer);
202
203 atimer->reprogram = 0; /* clear flag */
204 return updates;
205}
206
207/* look through the instance list and call period_elapsed if needed */
208static void ct_xfitimer_check_period(struct ct_timer *atimer)
209{
210 struct ct_timer_instance *ti;
211 unsigned long flags;
212
213 spin_lock_irqsave(&atimer->list_lock, flags);
214 list_for_each_entry(ti, &atimer->instance_head, instance_list) {
215 if (ti->need_update) {
216 ti->need_update = 0;
217 ti->apcm->interrupt(ti->apcm);
218 }
219 }
220 spin_unlock_irqrestore(&atimer->list_lock, flags);
221}
222
223/* Handle timer-interrupt */
224static void ct_xfitimer_callback(struct ct_timer *atimer)
225{
226 int update;
227 unsigned long flags;
228
229 spin_lock_irqsave(&atimer->lock, flags);
230 atimer->irq_handling = 1;
231 do {
232 update = ct_xfitimer_reprogram(atimer);
233 spin_unlock(&atimer->lock);
234 if (update)
235 ct_xfitimer_check_period(atimer);
236 spin_lock(&atimer->lock);
237 } while (atimer->reprogram);
238 atimer->irq_handling = 0;
239 spin_unlock_irqrestore(&atimer->lock, flags);
240}
241
242static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
243{
244 ti->frag_count = ti->substream->runtime->period_size;
245 ti->need_update = 0;
246}
247
248
249/* start/stop the timer */
250static void ct_xfitimer_update(struct ct_timer *atimer)
251{
252 unsigned long flags;
253 int update;
254
255 if (atimer->irq_handling) {
256 /* reached from IRQ handler; let it handle later */
257 atimer->reprogram = 1;
258 return;
259 }
260
261 spin_lock_irqsave(&atimer->lock, flags);
262 ct_xfitimer_irq_stop(atimer);
263 update = ct_xfitimer_reprogram(atimer);
264 spin_unlock_irqrestore(&atimer->lock, flags);
265 if (update)
266 ct_xfitimer_check_period(atimer);
267}
268
269static void ct_xfitimer_start(struct ct_timer_instance *ti)
270{
271 struct ct_timer *atimer = ti->timer_base;
272 unsigned long flags;
273
274 spin_lock_irqsave(&atimer->lock, flags);
275 list_add(&ti->running_list, &atimer->running_head);
276 spin_unlock_irqrestore(&atimer->lock, flags);
277 ct_xfitimer_update(atimer);
278}
279
280static void ct_xfitimer_stop(struct ct_timer_instance *ti)
281{
282 struct ct_timer *atimer = ti->timer_base;
283 unsigned long flags;
284
285 spin_lock_irqsave(&atimer->lock, flags);
286 list_del_init(&ti->running_list);
287 ti->need_update = 0;
288 spin_unlock_irqrestore(&atimer->lock, flags);
289 ct_xfitimer_update(atimer);
290}
291
292static void ct_xfitimer_free_global(struct ct_timer *atimer)
293{
294 ct_xfitimer_irq_stop(atimer);
295}
296
297static struct ct_timer_ops ct_xfitimer_ops = {
298 .prepare = ct_xfitimer_prepare,
299 .start = ct_xfitimer_start,
300 .stop = ct_xfitimer_stop,
301 .interrupt = ct_xfitimer_callback,
302 .free_global = ct_xfitimer_free_global,
303};
304
305/*
306 * timer instance
307 */
308
309struct ct_timer_instance *
310ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
311{
312 struct ct_timer_instance *ti;
313
314 ti = kzalloc(sizeof(*ti), GFP_KERNEL);
315 if (!ti)
316 return NULL;
317 spin_lock_init(&ti->lock);
318 INIT_LIST_HEAD(&ti->instance_list);
319 INIT_LIST_HEAD(&ti->running_list);
320 ti->timer_base = atimer;
321 ti->apcm = apcm;
322 ti->substream = apcm->substream;
323 if (atimer->ops->init)
324 atimer->ops->init(ti);
325
326 spin_lock_irq(&atimer->list_lock);
327 list_add(&ti->instance_list, &atimer->instance_head);
328 spin_unlock_irq(&atimer->list_lock);
329
330 return ti;
331}
332
333void ct_timer_prepare(struct ct_timer_instance *ti)
334{
335 if (ti->timer_base->ops->prepare)
336 ti->timer_base->ops->prepare(ti);
337 ti->position = 0;
338 ti->running = 0;
339}
340
341void ct_timer_start(struct ct_timer_instance *ti)
342{
343 struct ct_timer *atimer = ti->timer_base;
344 atimer->ops->start(ti);
345}
346
347void ct_timer_stop(struct ct_timer_instance *ti)
348{
349 struct ct_timer *atimer = ti->timer_base;
350 atimer->ops->stop(ti);
351}
352
353void ct_timer_instance_free(struct ct_timer_instance *ti)
354{
355 struct ct_timer *atimer = ti->timer_base;
356
357 atimer->ops->stop(ti); /* to be sure */
358 if (atimer->ops->free_instance)
359 atimer->ops->free_instance(ti);
360
361 spin_lock_irq(&atimer->list_lock);
362 list_del(&ti->instance_list);
363 spin_unlock_irq(&atimer->list_lock);
364
365 kfree(ti);
366}
367
368/*
369 * timer manager
370 */
371
372#define USE_SYSTEM_TIMER 0
373
374static void ct_timer_interrupt(void *data, unsigned int status)
375{
376 struct ct_timer *timer = data;
377
378 /* Interval timer interrupt */
379 if ((status & IT_INT) && timer->ops->interrupt)
380 timer->ops->interrupt(timer);
381}
382
383struct ct_timer *ct_timer_new(struct ct_atc *atc)
384{
385 struct ct_timer *atimer;
386 struct hw *hw;
387
388 atimer = kzalloc(sizeof(*atimer), GFP_KERNEL);
389 if (!atimer)
390 return NULL;
391 spin_lock_init(&atimer->lock);
392 spin_lock_init(&atimer->list_lock);
393 INIT_LIST_HEAD(&atimer->instance_head);
394 INIT_LIST_HEAD(&atimer->running_head);
395 atimer->atc = atc;
396 hw = atc->hw;
397 if (!USE_SYSTEM_TIMER && hw->set_timer_irq) {
398 printk(KERN_INFO "ctxfi: Use xfi-native timer\n");
399 atimer->ops = &ct_xfitimer_ops;
400 hw->irq_callback_data = atimer;
401 hw->irq_callback = ct_timer_interrupt;
402 } else {
403 printk(KERN_INFO "ctxfi: Use system timer\n");
404 atimer->ops = &ct_systimer_ops;
405 }
406 return atimer;
407}
408
409void ct_timer_free(struct ct_timer *atimer)
410{
411 struct hw *hw = atimer->atc->hw;
412 hw->irq_callback = NULL;
413 if (atimer->ops->free_global)
414 atimer->ops->free_global(atimer);
415 kfree(atimer);
416}
417