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