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
17static 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)(l: &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, data: 256 - ticks); /* timer 1 count */
28 snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, data: tmp); /* enable timer 1 IRQ */
29 snd_gf1_adlib_write(gus, reg: 0x04, data: tmp >> 2); /* timer 2 start */
30 return 0;
31}
32
33static 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)(l: &gus->reg_lock);
40 tmp = (gus->gf1.timer_enabled &= ~4);
41 snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, data: tmp); /* disable timer #1 */
42 return 0;
43}
44
45/*
46 * Timer 2 - 320us
47 */
48
49static 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)(l: &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, data: 256 - ticks); /* timer 2 count */
60 snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, data: tmp); /* enable timer 2 IRQ */
61 snd_gf1_adlib_write(gus, reg: 0x04, data: tmp >> 2); /* timer 2 start */
62 return 0;
63}
64
65static 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)(l: &gus->reg_lock);
72 tmp = (gus->gf1.timer_enabled &= ~8);
73 snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, data: tmp); /* disable timer #1 */
74 return 0;
75}
76
77/*
78
79 */
80
81static 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, ticks_left: timer->sticks);
88}
89
90static 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, ticks_left: timer->sticks);
97}
98
99/*
100
101 */
102
103static 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
112static 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
121static 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
127static 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
133void 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(card: gus->card, id: "GF1 timer", tid: &tid, rtimer: &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(card: gus->card, id: "GF1 timer", tid: &tid, rtimer: &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
169void 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(card: gus->card, device_data: gus->gf1.timer1);
174 gus->gf1.timer1 = NULL;
175 }
176 if (gus->gf1.timer2) {
177 snd_device_free(card: gus->card, device_data: gus->gf1.timer2);
178 gus->gf1.timer2 = NULL;
179 }
180}
181

source code of linux/sound/isa/gus/gus_timer.c