162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Lee Revell <rlrevell@joe-job.com>
462306a36Sopenharmony_ci *                   Clemens Ladisch <clemens@ladisch.de>
562306a36Sopenharmony_ci *                   Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Routines for control of EMU10K1 chips
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/time.h>
1162306a36Sopenharmony_ci#include <sound/core.h>
1262306a36Sopenharmony_ci#include <sound/emu10k1.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int snd_emu10k1_timer_start(struct snd_timer *timer)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct snd_emu10k1 *emu;
1762306a36Sopenharmony_ci	unsigned int delay;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	emu = snd_timer_chip(timer);
2062306a36Sopenharmony_ci	delay = timer->sticks - 1;
2162306a36Sopenharmony_ci	if (delay < 5 ) /* minimum time is 5 ticks */
2262306a36Sopenharmony_ci		delay = 5;
2362306a36Sopenharmony_ci	snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
2462306a36Sopenharmony_ci	outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
2562306a36Sopenharmony_ci	return 0;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int snd_emu10k1_timer_stop(struct snd_timer *timer)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct snd_emu10k1 *emu;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	emu = snd_timer_chip(timer);
3362306a36Sopenharmony_ci	snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic unsigned long snd_emu10k1_timer_c_resolution(struct snd_timer *timer)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct snd_emu10k1 *emu = snd_timer_chip(timer);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (emu->card_capabilities->emu_model &&
4262306a36Sopenharmony_ci	    emu->emu1010.word_clock == 44100)
4362306a36Sopenharmony_ci		return 22676;  // 1 sample @ 44.1 kHz = 22.675736...us
4462306a36Sopenharmony_ci	else
4562306a36Sopenharmony_ci		return 20833;  // 1 sample @ 48 kHz = 20.833...us
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer,
4962306a36Sopenharmony_ci					       unsigned long *num, unsigned long *den)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct snd_emu10k1 *emu = snd_timer_chip(timer);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	*num = 1;
5462306a36Sopenharmony_ci	if (emu->card_capabilities->emu_model)
5562306a36Sopenharmony_ci		*den = emu->emu1010.word_clock;
5662306a36Sopenharmony_ci	else
5762306a36Sopenharmony_ci		*den = 48000;
5862306a36Sopenharmony_ci	return 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic const struct snd_timer_hardware snd_emu10k1_timer_hw = {
6262306a36Sopenharmony_ci	.flags = SNDRV_TIMER_HW_AUTO,
6362306a36Sopenharmony_ci	.ticks = 1024,
6462306a36Sopenharmony_ci	.start = snd_emu10k1_timer_start,
6562306a36Sopenharmony_ci	.stop = snd_emu10k1_timer_stop,
6662306a36Sopenharmony_ci	.c_resolution = snd_emu10k1_timer_c_resolution,
6762306a36Sopenharmony_ci	.precise_resolution = snd_emu10k1_timer_precise_resolution,
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct snd_timer *timer = NULL;
7362306a36Sopenharmony_ci	struct snd_timer_id tid;
7462306a36Sopenharmony_ci	int err;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
7762306a36Sopenharmony_ci	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
7862306a36Sopenharmony_ci	tid.card = emu->card->number;
7962306a36Sopenharmony_ci	tid.device = device;
8062306a36Sopenharmony_ci	tid.subdevice = 0;
8162306a36Sopenharmony_ci	err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer);
8262306a36Sopenharmony_ci	if (err >= 0) {
8362306a36Sopenharmony_ci		strcpy(timer->name, "EMU10K1 timer");
8462306a36Sopenharmony_ci		timer->private_data = emu;
8562306a36Sopenharmony_ci		timer->hw = snd_emu10k1_timer_hw;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	emu->timer = timer;
8862306a36Sopenharmony_ci	return err;
8962306a36Sopenharmony_ci}
90