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