1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ISA DMA support functions |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
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(dmanr: dma); |
35 | clear_dma_ff(dmanr: dma); |
36 | set_dma_mode(dmanr: dma, mode); |
37 | set_dma_addr(dmanr: dma, a: addr); |
38 | set_dma_count(dmanr: dma, count: size); |
39 | if (!(mode & DMA_MODE_NO_ENABLE)) |
40 | enable_dma(dmanr: 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(dmanr: dma); |
57 | disable_dma(dmanr: 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(dmanr: dma); |
76 | if (!isa_dma_bridge_buggy) |
77 | disable_dma(dmanr: dma); |
78 | result = get_dma_residue(dmanr: 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(dmanr: dma); |
85 | if (!isa_dma_bridge_buggy) |
86 | enable_dma(dmanr: 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(dmanr: 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(dmanr: dma, device_id: name)) |
128 | return -EBUSY; |
129 | p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL); |
130 | if (!p) { |
131 | free_dma(dmanr: dma); |
132 | return -ENOMEM; |
133 | } |
134 | p->dma = dma; |
135 | devres_add(dev, res: p); |
136 | return 0; |
137 | } |
138 | EXPORT_SYMBOL_GPL(snd_devm_request_dma); |
139 | |