18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Lee Revell <rlrevell@joe-job.com> 48c2ecf20Sopenharmony_ci * Clemens Ladisch <clemens@ladisch.de> 58c2ecf20Sopenharmony_ci * Routines for control of EMU10K1 chips 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * BUGS: 88c2ecf20Sopenharmony_ci * -- 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * TODO: 118c2ecf20Sopenharmony_ci * -- 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/time.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/emu10k1.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int snd_emu10k1_timer_start(struct snd_timer *timer) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu; 218c2ecf20Sopenharmony_ci unsigned long flags; 228c2ecf20Sopenharmony_ci unsigned int delay; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci emu = snd_timer_chip(timer); 258c2ecf20Sopenharmony_ci delay = timer->sticks - 1; 268c2ecf20Sopenharmony_ci if (delay < 5 ) /* minimum time is 5 ticks */ 278c2ecf20Sopenharmony_ci delay = 5; 288c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 298c2ecf20Sopenharmony_ci snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB); 308c2ecf20Sopenharmony_ci outw(delay & TIMER_RATE_MASK, emu->port + TIMER); 318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int snd_emu10k1_timer_stop(struct snd_timer *timer) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu; 388c2ecf20Sopenharmony_ci unsigned long flags; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci emu = snd_timer_chip(timer); 418c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 428c2ecf20Sopenharmony_ci snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer, 488c2ecf20Sopenharmony_ci unsigned long *num, unsigned long *den) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci *num = 1; 518c2ecf20Sopenharmony_ci *den = 48000; 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct snd_timer_hardware snd_emu10k1_timer_hw = { 568c2ecf20Sopenharmony_ci .flags = SNDRV_TIMER_HW_AUTO, 578c2ecf20Sopenharmony_ci .resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */ 588c2ecf20Sopenharmony_ci .ticks = 1024, 598c2ecf20Sopenharmony_ci .start = snd_emu10k1_timer_start, 608c2ecf20Sopenharmony_ci .stop = snd_emu10k1_timer_stop, 618c2ecf20Sopenharmony_ci .precise_resolution = snd_emu10k1_timer_precise_resolution, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciint snd_emu10k1_timer(struct snd_emu10k1 *emu, int device) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct snd_timer *timer = NULL; 678c2ecf20Sopenharmony_ci struct snd_timer_id tid; 688c2ecf20Sopenharmony_ci int err; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_CARD; 718c2ecf20Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 728c2ecf20Sopenharmony_ci tid.card = emu->card->number; 738c2ecf20Sopenharmony_ci tid.device = device; 748c2ecf20Sopenharmony_ci tid.subdevice = 0; 758c2ecf20Sopenharmony_ci if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) { 768c2ecf20Sopenharmony_ci strcpy(timer->name, "EMU10K1 timer"); 778c2ecf20Sopenharmony_ci timer->private_data = emu; 788c2ecf20Sopenharmony_ci timer->hw = snd_emu10k1_timer_hw; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci emu->timer = timer; 818c2ecf20Sopenharmony_ci return err; 828c2ecf20Sopenharmony_ci} 83