FILE
cttimer.c
File preview with syntax highlighting.
linux-6.19.14/sound/pci/ctxfi/cttimer.c
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 15static bool use_system_timer; 16MODULE_PARM_DESC(use_system_timer, "Force to use system-timer"); 17module_param(use_system_timer, bool, 0444); 18 19struct 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 */ 30struct 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 */ 45struct 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 63static 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 89static void ct_systimer_init(struct ct_timer_instance *ti) 90{ 91 timer_setup(&ti->timer, ct_systimer_callback, 0); 92} 93 94static 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 105static 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 112static 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 120static 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 137static 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 148static 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 158static 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 */ 172static 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 */ 222static 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 */ 236static 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 252static 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 */ 261static 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 274static 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 288static 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 299static void ct_xfitimer_free_global(struct ct_timer *atimer) 300{ 301 ct_xfitimer_irq_stop(atimer); 302} 303 304static 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 316struct ct_timer_instance * 317ct_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 340void 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 348void ct_timer_start(struct ct_timer_instance *ti) 349{ 350 struct ct_timer *atimer = ti->timer_base; 351 atimer->ops->start(ti); 352} 353 354void ct_timer_stop(struct ct_timer_instance *ti) 355{ 356 struct ct_timer *atimer = ti->timer_base; 357 atimer->ops->stop(ti); 358} 359 360void 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 379static 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 388struct 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 414void 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
~/
Read-only file preview.