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