brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 0bb447c Raw
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