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, 256 - ticks); /* timer 1 count */ 29 snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */ 30 snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ 31 spin_unlock_irqrestore(&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, tmp); /* disable timer #1 */ 45 spin_unlock_irqrestore(&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, 256 - ticks); /* timer 2 count */ 65 snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */ 66 snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ 67 spin_unlock_irqrestore(&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, tmp); /* disable timer #1 */ 81 spin_unlock_irqrestore(&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, 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, 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(gus->card, "GF1 timer", &tid, &timer) >= 0) { 159 strcpy(timer->name, "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(gus->card, "GF1 timer", &tid, &timer) >= 0) { 169 strcpy(timer->name, "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(gus->card, gus->gf1.timer1); 182 gus->gf1.timer1 = NULL; 183 } 184 if (gus->gf1.timer2) { 185 snd_device_free(gus->card, gus->gf1.timer2); 186 gus->gf1.timer2 = NULL; 187 } 188 } 189
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.