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