xref: /kernel/linux/linux-5.10/sound/isa/gus/gus_mem.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4 *  GUS's memory allocation routines / bottom layer
5 */
6
7#include <linux/slab.h>
8#include <linux/string.h>
9#include <sound/core.h>
10#include <sound/gus.h>
11#include <sound/info.h>
12
13#ifdef CONFIG_SND_DEBUG
14static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
15				  struct snd_info_buffer *buffer);
16#endif
17
18void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup)
19{
20	if (!xup) {
21		mutex_lock(&alloc->memory_mutex);
22	} else {
23		mutex_unlock(&alloc->memory_mutex);
24	}
25}
26
27static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
28					       struct snd_gf1_mem_block * block)
29{
30	struct snd_gf1_mem_block *pblock, *nblock;
31
32	nblock = kmalloc(sizeof(struct snd_gf1_mem_block), GFP_KERNEL);
33	if (nblock == NULL)
34		return NULL;
35	*nblock = *block;
36	pblock = alloc->first;
37	while (pblock) {
38		if (pblock->ptr > nblock->ptr) {
39			nblock->prev = pblock->prev;
40			nblock->next = pblock;
41			pblock->prev = nblock;
42			if (pblock == alloc->first)
43				alloc->first = nblock;
44			else
45				nblock->prev->next = nblock;
46			mutex_unlock(&alloc->memory_mutex);
47			return NULL;
48		}
49		pblock = pblock->next;
50	}
51	nblock->next = NULL;
52	if (alloc->last == NULL) {
53		nblock->prev = NULL;
54		alloc->first = alloc->last = nblock;
55	} else {
56		nblock->prev = alloc->last;
57		alloc->last->next = nblock;
58		alloc->last = nblock;
59	}
60	return nblock;
61}
62
63int snd_gf1_mem_xfree(struct snd_gf1_mem * alloc, struct snd_gf1_mem_block * block)
64{
65	if (block->share) {	/* ok.. shared block */
66		block->share--;
67		mutex_unlock(&alloc->memory_mutex);
68		return 0;
69	}
70	if (alloc->first == block) {
71		alloc->first = block->next;
72		if (block->next)
73			block->next->prev = NULL;
74	} else {
75		block->prev->next = block->next;
76		if (block->next)
77			block->next->prev = block->prev;
78	}
79	if (alloc->last == block) {
80		alloc->last = block->prev;
81		if (block->prev)
82			block->prev->next = NULL;
83	} else {
84		block->next->prev = block->prev;
85		if (block->prev)
86			block->prev->next = block->next;
87	}
88	kfree(block->name);
89	kfree(block);
90	return 0;
91}
92
93static struct snd_gf1_mem_block *snd_gf1_mem_look(struct snd_gf1_mem * alloc,
94					     unsigned int address)
95{
96	struct snd_gf1_mem_block *block;
97
98	for (block = alloc->first; block; block = block->next) {
99		if (block->ptr == address) {
100			return block;
101		}
102	}
103	return NULL;
104}
105
106static struct snd_gf1_mem_block *snd_gf1_mem_share(struct snd_gf1_mem * alloc,
107					      unsigned int *share_id)
108{
109	struct snd_gf1_mem_block *block;
110
111	if (!share_id[0] && !share_id[1] &&
112	    !share_id[2] && !share_id[3])
113		return NULL;
114	for (block = alloc->first; block; block = block->next)
115		if (!memcmp(share_id, block->share_id,
116				sizeof(block->share_id)))
117			return block;
118	return NULL;
119}
120
121static int snd_gf1_mem_find(struct snd_gf1_mem * alloc,
122			    struct snd_gf1_mem_block * block,
123			    unsigned int size, int w_16, int align)
124{
125	struct snd_gf1_bank_info *info = w_16 ? alloc->banks_16 : alloc->banks_8;
126	unsigned int idx, boundary;
127	int size1;
128	struct snd_gf1_mem_block *pblock;
129	unsigned int ptr1, ptr2;
130
131	if (w_16 && align < 2)
132		align = 2;
133	block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0;
134	block->owner = SNDRV_GF1_MEM_OWNER_DRIVER;
135	block->share = 0;
136	block->share_id[0] = block->share_id[1] =
137	block->share_id[2] = block->share_id[3] = 0;
138	block->name = NULL;
139	block->prev = block->next = NULL;
140	for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) {
141		while (pblock->ptr >= (boundary = info[idx].address + info[idx].size))
142			idx++;
143		while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size))
144			idx++;
145		ptr2 = boundary;
146		if (pblock->next) {
147			if (pblock->ptr + pblock->size == pblock->next->ptr)
148				continue;
149			if (pblock->next->ptr < boundary)
150				ptr2 = pblock->next->ptr;
151		}
152		ptr1 = ALIGN(pblock->ptr + pblock->size, align);
153		if (ptr1 >= ptr2)
154			continue;
155		size1 = ptr2 - ptr1;
156		if ((int)size <= size1) {
157			block->ptr = ptr1;
158			block->size = size;
159			return 0;
160		}
161	}
162	while (++idx < 4) {
163		if (size <= info[idx].size) {
164			/* I assume that bank address is already aligned.. */
165			block->ptr = info[idx].address;
166			block->size = size;
167			return 0;
168		}
169	}
170	return -ENOMEM;
171}
172
173struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owner,
174				       char *name, int size, int w_16, int align,
175				       unsigned int *share_id)
176{
177	struct snd_gf1_mem_block block, *nblock;
178
179	snd_gf1_mem_lock(alloc, 0);
180	if (share_id != NULL) {
181		nblock = snd_gf1_mem_share(alloc, share_id);
182		if (nblock != NULL) {
183			if (size != (int)nblock->size) {
184				/* TODO: remove in the future */
185				snd_printk(KERN_ERR "snd_gf1_mem_alloc - share: sizes differ\n");
186				goto __std;
187			}
188			nblock->share++;
189			snd_gf1_mem_lock(alloc, 1);
190			return NULL;
191		}
192	}
193      __std:
194	if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) {
195		snd_gf1_mem_lock(alloc, 1);
196		return NULL;
197	}
198	if (share_id != NULL)
199		memcpy(&block.share_id, share_id, sizeof(block.share_id));
200	block.owner = owner;
201	block.name = kstrdup(name, GFP_KERNEL);
202	nblock = snd_gf1_mem_xalloc(alloc, &block);
203	snd_gf1_mem_lock(alloc, 1);
204	return nblock;
205}
206
207int snd_gf1_mem_free(struct snd_gf1_mem * alloc, unsigned int address)
208{
209	int result;
210	struct snd_gf1_mem_block *block;
211
212	snd_gf1_mem_lock(alloc, 0);
213	if ((block = snd_gf1_mem_look(alloc, address)) != NULL) {
214		result = snd_gf1_mem_xfree(alloc, block);
215		snd_gf1_mem_lock(alloc, 1);
216		return result;
217	}
218	snd_gf1_mem_lock(alloc, 1);
219	return -EINVAL;
220}
221
222int snd_gf1_mem_init(struct snd_gus_card * gus)
223{
224	struct snd_gf1_mem *alloc;
225	struct snd_gf1_mem_block block;
226
227	alloc = &gus->gf1.mem_alloc;
228	mutex_init(&alloc->memory_mutex);
229	alloc->first = alloc->last = NULL;
230	if (!gus->gf1.memory)
231		return 0;
232
233	memset(&block, 0, sizeof(block));
234	block.owner = SNDRV_GF1_MEM_OWNER_DRIVER;
235	if (gus->gf1.enh_mode) {
236		block.ptr = 0;
237		block.size = 1024;
238		block.name = kstrdup("InterWave LFOs", GFP_KERNEL);
239		if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
240			return -ENOMEM;
241	}
242	block.ptr = gus->gf1.default_voice_address;
243	block.size = 4;
244	block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL);
245	if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
246		return -ENOMEM;
247#ifdef CONFIG_SND_DEBUG
248	snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read);
249#endif
250	return 0;
251}
252
253int snd_gf1_mem_done(struct snd_gus_card * gus)
254{
255	struct snd_gf1_mem *alloc;
256	struct snd_gf1_mem_block *block, *nblock;
257
258	alloc = &gus->gf1.mem_alloc;
259	block = alloc->first;
260	while (block) {
261		nblock = block->next;
262		snd_gf1_mem_xfree(alloc, block);
263		block = nblock;
264	}
265	return 0;
266}
267
268#ifdef CONFIG_SND_DEBUG
269static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
270				  struct snd_info_buffer *buffer)
271{
272	struct snd_gus_card *gus;
273	struct snd_gf1_mem *alloc;
274	struct snd_gf1_mem_block *block;
275	unsigned int total, used;
276	int i;
277
278	gus = entry->private_data;
279	alloc = &gus->gf1.mem_alloc;
280	mutex_lock(&alloc->memory_mutex);
281	snd_iprintf(buffer, "8-bit banks       : \n    ");
282	for (i = 0; i < 4; i++)
283		snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : "");
284	snd_iprintf(buffer, "\n"
285		    "16-bit banks      : \n    ");
286	for (i = total = 0; i < 4; i++) {
287		snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : "");
288		total += alloc->banks_16[i].size;
289	}
290	snd_iprintf(buffer, "\n");
291	used = 0;
292	for (block = alloc->first, i = 0; block; block = block->next, i++) {
293		used += block->size;
294		snd_iprintf(buffer, "Block %i onboard 0x%x size %i (0x%x):\n", i, block->ptr, block->size, block->size);
295		if (block->share ||
296		    block->share_id[0] || block->share_id[1] ||
297		    block->share_id[2] || block->share_id[3])
298			snd_iprintf(buffer, "  Share           : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n",
299				block->share,
300				block->share_id[0], block->share_id[1],
301				block->share_id[2], block->share_id[3]);
302		snd_iprintf(buffer, "  Flags           :%s\n",
303		block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : "");
304		snd_iprintf(buffer, "  Owner           : ");
305		switch (block->owner) {
306		case SNDRV_GF1_MEM_OWNER_DRIVER:
307			snd_iprintf(buffer, "driver - %s\n", block->name);
308			break;
309		case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE:
310			snd_iprintf(buffer, "SIMPLE wave\n");
311			break;
312		case SNDRV_GF1_MEM_OWNER_WAVE_GF1:
313			snd_iprintf(buffer, "GF1 wave\n");
314			break;
315		case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF:
316			snd_iprintf(buffer, "IWFFFF wave\n");
317			break;
318		default:
319			snd_iprintf(buffer, "unknown\n");
320		}
321	}
322	snd_iprintf(buffer, "  Total: memory = %i, used = %i, free = %i\n",
323		    total, used, total - used);
324	mutex_unlock(&alloc->memory_mutex);
325#if 0
326	ultra_iprintf(buffer, "  Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n",
327		      ultra_memory_free_size(card, &card->gf1.mem_alloc),
328		  ultra_memory_free_block(card, &card->gf1.mem_alloc, 0),
329		 ultra_memory_free_block(card, &card->gf1.mem_alloc, 1));
330#endif
331}
332#endif
333