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