Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/core/isadma.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* ISA DMA support functions
4
* Copyright (c) by Jaroslav Kysela <[email protected]>
5
*/
6
7
/*
8
* Defining following add some delay. Maybe this helps for some broken
9
* ISA DMA controllers.
10
*/
11
12
#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
13
14
#include <linux/export.h>
15
#include <linux/isa-dma.h>
16
#include <sound/core.h>
17
18
/**
19
* snd_dma_program - program an ISA DMA transfer
20
* @dma: the dma number
21
* @addr: the physical address of the buffer
22
* @size: the DMA transfer size
23
* @mode: the DMA transfer mode, DMA_MODE_XXX
24
*
25
* Programs an ISA DMA transfer for the given buffer.
26
*/
27
void snd_dma_program(unsigned long dma,
28
unsigned long addr, unsigned int size,
29
unsigned short mode)
30
{
31
unsigned long flags;
32
33
flags = claim_dma_lock();
34
disable_dma(dma);
35
clear_dma_ff(dma);
36
set_dma_mode(dma, mode);
37
set_dma_addr(dma, addr);
38
set_dma_count(dma, size);
39
if (!(mode & DMA_MODE_NO_ENABLE))
40
enable_dma(dma);
41
release_dma_lock(flags);
42
}
43
EXPORT_SYMBOL(snd_dma_program);
44
45
/**
46
* snd_dma_disable - stop the ISA DMA transfer
47
* @dma: the dma number
48
*
49
* Stops the ISA DMA transfer.
50
*/
51
void snd_dma_disable(unsigned long dma)
52
{
53
unsigned long flags;
54
55
flags = claim_dma_lock();
56
clear_dma_ff(dma);
57
disable_dma(dma);
58
release_dma_lock(flags);
59
}
60
EXPORT_SYMBOL(snd_dma_disable);
61
62
/**
63
* snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes
64
* @dma: the dma number
65
* @size: the dma transfer size
66
*
67
* Return: The current pointer in DMA transfer buffer in bytes.
68
*/
69
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
70
{
71
unsigned long flags;
72
unsigned int result, result1;
73
74
flags = claim_dma_lock();
75
clear_dma_ff(dma);
76
if (!isa_dma_bridge_buggy)
77
disable_dma(dma);
78
result = get_dma_residue(dma);
79
/*
80
* HACK - read the counter again and choose higher value in order to
81
* avoid reading during counter lower byte roll over if the
82
* isa_dma_bridge_buggy is set.
83
*/
84
result1 = get_dma_residue(dma);
85
if (!isa_dma_bridge_buggy)
86
enable_dma(dma);
87
release_dma_lock(flags);
88
if (unlikely(result < result1))
89
result = result1;
90
#ifdef CONFIG_SND_DEBUG
91
if (result > size)
92
pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
93
#endif
94
if (result >= size || result == 0)
95
return 0;
96
else
97
return size - result;
98
}
99
EXPORT_SYMBOL(snd_dma_pointer);
100
101
struct snd_dma_data {
102
int dma;
103
};
104
105
static void __snd_release_dma(struct device *dev, void *data)
106
{
107
struct snd_dma_data *p = data;
108
109
snd_dma_disable(p->dma);
110
free_dma(p->dma);
111
}
112
113
/**
114
* snd_devm_request_dma - the managed version of request_dma()
115
* @dev: the device pointer
116
* @dma: the dma number
117
* @name: the name string of the requester
118
*
119
* The requested DMA will be automatically released at unbinding via devres.
120
*
121
* Return: zero on success, or a negative error code
122
*/
123
int snd_devm_request_dma(struct device *dev, int dma, const char *name)
124
{
125
struct snd_dma_data *p;
126
127
if (request_dma(dma, name))
128
return -EBUSY;
129
p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL);
130
if (!p) {
131
free_dma(dma);
132
return -ENOMEM;
133
}
134
p->dma = dma;
135
devres_add(dev, p);
136
return 0;
137
}
138
EXPORT_SYMBOL_GPL(snd_devm_request_dma);
139
140