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