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