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