Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/isa/gus/gus_dma.c
26426 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Routines for GF1 DMA control
4
* Copyright (c) by Jaroslav Kysela <[email protected]>
5
*/
6
7
#include <asm/dma.h>
8
#include <linux/slab.h>
9
#include <sound/core.h>
10
#include <sound/gus.h>
11
12
static void snd_gf1_dma_ack(struct snd_gus_card * gus)
13
{
14
unsigned long flags;
15
16
spin_lock_irqsave(&gus->reg_lock, flags);
17
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
18
snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
19
spin_unlock_irqrestore(&gus->reg_lock, flags);
20
}
21
22
static void snd_gf1_dma_program(struct snd_gus_card * gus,
23
unsigned int addr,
24
unsigned long buf_addr,
25
unsigned int count,
26
unsigned int cmd)
27
{
28
unsigned long flags;
29
unsigned int address;
30
unsigned char dma_cmd;
31
unsigned int address_high;
32
33
dev_dbg(gus->card->dev,
34
"dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
35
addr, buf_addr, count);
36
37
if (gus->gf1.dma1 > 3) {
38
if (gus->gf1.enh_mode) {
39
address = addr >> 1;
40
} else {
41
if (addr & 0x1f) {
42
dev_dbg(gus->card->dev,
43
"%s: unaligned address (0x%x)?\n",
44
__func__, addr);
45
return;
46
}
47
address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
48
}
49
} else {
50
address = addr;
51
}
52
53
dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
54
#if 0
55
dma_cmd |= 0x08;
56
#endif
57
if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
58
count++;
59
count &= ~1; /* align */
60
}
61
if (gus->gf1.dma1 > 3) {
62
dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
63
count++;
64
count &= ~1; /* align */
65
}
66
snd_gf1_dma_ack(gus);
67
snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
68
#if 0
69
dev_dbg(gus->card->dev,
70
"address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
71
address << 1, count, dma_cmd);
72
#endif
73
spin_lock_irqsave(&gus->reg_lock, flags);
74
if (gus->gf1.enh_mode) {
75
address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
76
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
77
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
78
} else
79
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
80
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
81
spin_unlock_irqrestore(&gus->reg_lock, flags);
82
}
83
84
static struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)
85
{
86
struct snd_gf1_dma_block *block;
87
88
/* PCM block have bigger priority than synthesizer one */
89
if (gus->gf1.dma_data_pcm) {
90
block = gus->gf1.dma_data_pcm;
91
if (gus->gf1.dma_data_pcm_last == block) {
92
gus->gf1.dma_data_pcm =
93
gus->gf1.dma_data_pcm_last = NULL;
94
} else {
95
gus->gf1.dma_data_pcm = block->next;
96
}
97
} else if (gus->gf1.dma_data_synth) {
98
block = gus->gf1.dma_data_synth;
99
if (gus->gf1.dma_data_synth_last == block) {
100
gus->gf1.dma_data_synth =
101
gus->gf1.dma_data_synth_last = NULL;
102
} else {
103
gus->gf1.dma_data_synth = block->next;
104
}
105
} else {
106
block = NULL;
107
}
108
if (block) {
109
gus->gf1.dma_ack = block->ack;
110
gus->gf1.dma_private_data = block->private_data;
111
}
112
return block;
113
}
114
115
116
static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
117
{
118
struct snd_gf1_dma_block *block;
119
120
snd_gf1_dma_ack(gus);
121
if (gus->gf1.dma_ack)
122
gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
123
spin_lock(&gus->dma_lock);
124
if (gus->gf1.dma_data_pcm == NULL &&
125
gus->gf1.dma_data_synth == NULL) {
126
gus->gf1.dma_ack = NULL;
127
gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
128
spin_unlock(&gus->dma_lock);
129
return;
130
}
131
block = snd_gf1_dma_next_block(gus);
132
spin_unlock(&gus->dma_lock);
133
if (!block)
134
return;
135
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
136
kfree(block);
137
#if 0
138
dev_dbg(gus->card->dev,
139
"program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
140
block->addr, block->buf_addr, block->count, block->cmd);
141
#endif
142
}
143
144
int snd_gf1_dma_init(struct snd_gus_card * gus)
145
{
146
mutex_lock(&gus->dma_mutex);
147
gus->gf1.dma_shared++;
148
if (gus->gf1.dma_shared > 1) {
149
mutex_unlock(&gus->dma_mutex);
150
return 0;
151
}
152
gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
153
gus->gf1.dma_data_pcm =
154
gus->gf1.dma_data_pcm_last =
155
gus->gf1.dma_data_synth =
156
gus->gf1.dma_data_synth_last = NULL;
157
mutex_unlock(&gus->dma_mutex);
158
return 0;
159
}
160
161
int snd_gf1_dma_done(struct snd_gus_card * gus)
162
{
163
struct snd_gf1_dma_block *block;
164
165
mutex_lock(&gus->dma_mutex);
166
gus->gf1.dma_shared--;
167
if (!gus->gf1.dma_shared) {
168
snd_dma_disable(gus->gf1.dma1);
169
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
170
snd_gf1_dma_ack(gus);
171
while ((block = gus->gf1.dma_data_pcm)) {
172
gus->gf1.dma_data_pcm = block->next;
173
kfree(block);
174
}
175
while ((block = gus->gf1.dma_data_synth)) {
176
gus->gf1.dma_data_synth = block->next;
177
kfree(block);
178
}
179
gus->gf1.dma_data_pcm_last =
180
gus->gf1.dma_data_synth_last = NULL;
181
}
182
mutex_unlock(&gus->dma_mutex);
183
return 0;
184
}
185
186
int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
187
struct snd_gf1_dma_block * __block,
188
int atomic,
189
int synth)
190
{
191
unsigned long flags;
192
struct snd_gf1_dma_block *block;
193
194
block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
195
if (!block)
196
return -ENOMEM;
197
198
*block = *__block;
199
block->next = NULL;
200
201
dev_dbg(gus->card->dev,
202
"addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
203
block->addr, (long) block->buffer, block->count,
204
block->cmd);
205
206
dev_dbg(gus->card->dev,
207
"gus->gf1.dma_data_pcm_last = 0x%lx\n",
208
(long)gus->gf1.dma_data_pcm_last);
209
dev_dbg(gus->card->dev,
210
"gus->gf1.dma_data_pcm = 0x%lx\n",
211
(long)gus->gf1.dma_data_pcm);
212
213
spin_lock_irqsave(&gus->dma_lock, flags);
214
if (synth) {
215
if (gus->gf1.dma_data_synth_last) {
216
gus->gf1.dma_data_synth_last->next = block;
217
gus->gf1.dma_data_synth_last = block;
218
} else {
219
gus->gf1.dma_data_synth =
220
gus->gf1.dma_data_synth_last = block;
221
}
222
} else {
223
if (gus->gf1.dma_data_pcm_last) {
224
gus->gf1.dma_data_pcm_last->next = block;
225
gus->gf1.dma_data_pcm_last = block;
226
} else {
227
gus->gf1.dma_data_pcm =
228
gus->gf1.dma_data_pcm_last = block;
229
}
230
}
231
if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
232
gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
233
block = snd_gf1_dma_next_block(gus);
234
spin_unlock_irqrestore(&gus->dma_lock, flags);
235
if (block == NULL)
236
return 0;
237
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
238
kfree(block);
239
return 0;
240
}
241
spin_unlock_irqrestore(&gus->dma_lock, flags);
242
return 0;
243
}
244
245