440 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PCM timer handling on ctxfi4 */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 updates61 */62 63static void ct_systimer_callback(struct timer_list *t)64{65 struct ct_timer_instance *ti = from_timer(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 recording82 * 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 91static void ct_systimer_init(struct ct_timer_instance *ti)92{93 timer_setup(&ti->timer, ct_systimer_callback, 0);94}95 96static 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 109static 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 del_timer(&ti->timer);116 spin_unlock_irqrestore(&ti->lock, flags);117}118 119static void ct_systimer_prepare(struct ct_timer_instance *ti)120{121 ct_systimer_stop(ti);122 try_to_del_timer_sync(&ti->timer);123}124 125#define ct_systimer_free ct_systimer_prepare126 127static 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 irq138 */139 140#define CT_TIMER_FREQ 48000141#define MIN_TICKS 1142#define MAX_TICKS ((1 << 13) - 1)143 144static 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 155static 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 165static 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 streams175 * to call snd_pcm_period_elapsed() appropriately176 *177 * call this inside the lock and irq disabled178 */179static 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 */229static 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 */245static 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 263static 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 */272static 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 289static 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 304static 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 316static void ct_xfitimer_free_global(struct ct_timer *atimer)317{318 ct_xfitimer_irq_stop(atimer);319}320 321static 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 instance331 */332 333struct ct_timer_instance *334ct_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 357void 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 365void ct_timer_start(struct ct_timer_instance *ti)366{367 struct ct_timer *atimer = ti->timer_base;368 atimer->ops->start(ti);369}370 371void ct_timer_stop(struct ct_timer_instance *ti)372{373 struct ct_timer *atimer = ti->timer_base;374 atimer->ops->stop(ti);375}376 377void 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 manager394 */395 396static 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 405struct 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 431void 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