1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Routines for Gravis UltraSound soundcards - Timers
4*4882a593Smuzhiyun * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * GUS have similar timers as AdLib (OPL2/OPL3 chips).
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/time.h>
10*4882a593Smuzhiyun #include <sound/core.h>
11*4882a593Smuzhiyun #include <sound/gus.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun /*
14*4882a593Smuzhiyun * Timer 1 - 80us
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
snd_gf1_timer1_start(struct snd_timer * timer)17*4882a593Smuzhiyun static int snd_gf1_timer1_start(struct snd_timer * timer)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun unsigned long flags;
20*4882a593Smuzhiyun unsigned char tmp;
21*4882a593Smuzhiyun unsigned int ticks;
22*4882a593Smuzhiyun struct snd_gus_card *gus;
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun gus = snd_timer_chip(timer);
25*4882a593Smuzhiyun spin_lock_irqsave(&gus->reg_lock, flags);
26*4882a593Smuzhiyun ticks = timer->sticks;
27*4882a593Smuzhiyun tmp = (gus->gf1.timer_enabled |= 4);
28*4882a593Smuzhiyun snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */
29*4882a593Smuzhiyun snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */
30*4882a593Smuzhiyun snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
31*4882a593Smuzhiyun spin_unlock_irqrestore(&gus->reg_lock, flags);
32*4882a593Smuzhiyun return 0;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
snd_gf1_timer1_stop(struct snd_timer * timer)35*4882a593Smuzhiyun static int snd_gf1_timer1_stop(struct snd_timer * timer)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun unsigned long flags;
38*4882a593Smuzhiyun unsigned char tmp;
39*4882a593Smuzhiyun struct snd_gus_card *gus;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun gus = snd_timer_chip(timer);
42*4882a593Smuzhiyun spin_lock_irqsave(&gus->reg_lock, flags);
43*4882a593Smuzhiyun tmp = (gus->gf1.timer_enabled &= ~4);
44*4882a593Smuzhiyun snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
45*4882a593Smuzhiyun spin_unlock_irqrestore(&gus->reg_lock, flags);
46*4882a593Smuzhiyun return 0;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /*
50*4882a593Smuzhiyun * Timer 2 - 320us
51*4882a593Smuzhiyun */
52*4882a593Smuzhiyun
snd_gf1_timer2_start(struct snd_timer * timer)53*4882a593Smuzhiyun static int snd_gf1_timer2_start(struct snd_timer * timer)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun unsigned long flags;
56*4882a593Smuzhiyun unsigned char tmp;
57*4882a593Smuzhiyun unsigned int ticks;
58*4882a593Smuzhiyun struct snd_gus_card *gus;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun gus = snd_timer_chip(timer);
61*4882a593Smuzhiyun spin_lock_irqsave(&gus->reg_lock, flags);
62*4882a593Smuzhiyun ticks = timer->sticks;
63*4882a593Smuzhiyun tmp = (gus->gf1.timer_enabled |= 8);
64*4882a593Smuzhiyun snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */
65*4882a593Smuzhiyun snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */
66*4882a593Smuzhiyun snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
67*4882a593Smuzhiyun spin_unlock_irqrestore(&gus->reg_lock, flags);
68*4882a593Smuzhiyun return 0;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
snd_gf1_timer2_stop(struct snd_timer * timer)71*4882a593Smuzhiyun static int snd_gf1_timer2_stop(struct snd_timer * timer)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun unsigned long flags;
74*4882a593Smuzhiyun unsigned char tmp;
75*4882a593Smuzhiyun struct snd_gus_card *gus;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun gus = snd_timer_chip(timer);
78*4882a593Smuzhiyun spin_lock_irqsave(&gus->reg_lock, flags);
79*4882a593Smuzhiyun tmp = (gus->gf1.timer_enabled &= ~8);
80*4882a593Smuzhiyun snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
81*4882a593Smuzhiyun spin_unlock_irqrestore(&gus->reg_lock, flags);
82*4882a593Smuzhiyun return 0;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /*
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun */
88*4882a593Smuzhiyun
snd_gf1_interrupt_timer1(struct snd_gus_card * gus)89*4882a593Smuzhiyun static void snd_gf1_interrupt_timer1(struct snd_gus_card * gus)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun struct snd_timer *timer = gus->gf1.timer1;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun if (timer == NULL)
94*4882a593Smuzhiyun return;
95*4882a593Smuzhiyun snd_timer_interrupt(timer, timer->sticks);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
snd_gf1_interrupt_timer2(struct snd_gus_card * gus)98*4882a593Smuzhiyun static void snd_gf1_interrupt_timer2(struct snd_gus_card * gus)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun struct snd_timer *timer = gus->gf1.timer2;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun if (timer == NULL)
103*4882a593Smuzhiyun return;
104*4882a593Smuzhiyun snd_timer_interrupt(timer, timer->sticks);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun /*
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun */
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun static const struct snd_timer_hardware snd_gf1_timer1 =
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun .flags = SNDRV_TIMER_HW_STOP,
114*4882a593Smuzhiyun .resolution = 80000,
115*4882a593Smuzhiyun .ticks = 256,
116*4882a593Smuzhiyun .start = snd_gf1_timer1_start,
117*4882a593Smuzhiyun .stop = snd_gf1_timer1_stop,
118*4882a593Smuzhiyun };
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun static const struct snd_timer_hardware snd_gf1_timer2 =
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun .flags = SNDRV_TIMER_HW_STOP,
123*4882a593Smuzhiyun .resolution = 320000,
124*4882a593Smuzhiyun .ticks = 256,
125*4882a593Smuzhiyun .start = snd_gf1_timer2_start,
126*4882a593Smuzhiyun .stop = snd_gf1_timer2_stop,
127*4882a593Smuzhiyun };
128*4882a593Smuzhiyun
snd_gf1_timer1_free(struct snd_timer * timer)129*4882a593Smuzhiyun static void snd_gf1_timer1_free(struct snd_timer *timer)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun struct snd_gus_card *gus = timer->private_data;
132*4882a593Smuzhiyun gus->gf1.timer1 = NULL;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
snd_gf1_timer2_free(struct snd_timer * timer)135*4882a593Smuzhiyun static void snd_gf1_timer2_free(struct snd_timer *timer)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct snd_gus_card *gus = timer->private_data;
138*4882a593Smuzhiyun gus->gf1.timer2 = NULL;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
snd_gf1_timers_init(struct snd_gus_card * gus)141*4882a593Smuzhiyun void snd_gf1_timers_init(struct snd_gus_card * gus)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun struct snd_timer *timer;
144*4882a593Smuzhiyun struct snd_timer_id tid;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL)
147*4882a593Smuzhiyun return;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1;
150*4882a593Smuzhiyun gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun tid.dev_class = SNDRV_TIMER_CLASS_CARD;
153*4882a593Smuzhiyun tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
154*4882a593Smuzhiyun tid.card = gus->card->number;
155*4882a593Smuzhiyun tid.device = gus->timer_dev;
156*4882a593Smuzhiyun tid.subdevice = 0;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
159*4882a593Smuzhiyun strcpy(timer->name, "GF1 timer #1");
160*4882a593Smuzhiyun timer->private_data = gus;
161*4882a593Smuzhiyun timer->private_free = snd_gf1_timer1_free;
162*4882a593Smuzhiyun timer->hw = snd_gf1_timer1;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun gus->gf1.timer1 = timer;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun tid.device++;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
169*4882a593Smuzhiyun strcpy(timer->name, "GF1 timer #2");
170*4882a593Smuzhiyun timer->private_data = gus;
171*4882a593Smuzhiyun timer->private_free = snd_gf1_timer2_free;
172*4882a593Smuzhiyun timer->hw = snd_gf1_timer2;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun gus->gf1.timer2 = timer;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
snd_gf1_timers_done(struct snd_gus_card * gus)177*4882a593Smuzhiyun void snd_gf1_timers_done(struct snd_gus_card * gus)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2);
180*4882a593Smuzhiyun if (gus->gf1.timer1) {
181*4882a593Smuzhiyun snd_device_free(gus->card, gus->gf1.timer1);
182*4882a593Smuzhiyun gus->gf1.timer1 = NULL;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun if (gus->gf1.timer2) {
185*4882a593Smuzhiyun snd_device_free(gus->card, gus->gf1.timer2);
186*4882a593Smuzhiyun gus->gf1.timer2 = NULL;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun }
189