1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 4 * Copyright (c) by Takashi Iwai <tiwai@suse.de> 5 * Copyright (c) by Scott McNab <sdm@fractalgraphics.com.au> 6 * 7 * Trident 4DWave-NX memory page allocation (TLB area) 8 * Trident chip can handle only 16MByte of the memory at the same time. 9 */ 10 11 #include <linux/io.h> 12 #include <linux/pci.h> 13 #include <linux/time.h> 14 #include <linux/mutex.h> 15 16 #include <sound/core.h> 17 #include "trident.h" 18 19 /* page arguments of these two macros are Trident page (4096 bytes), not like 20 * aligned pages in others 21 */ 22 #define __set_tlb_bus(trident,page,addr) \ 23 (trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1)) 24 #define __tlb_to_addr(trident,page) \ 25 (dma_addr_t)le32_to_cpu((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1)) 26 27 #if PAGE_SIZE == 4096 28 /* page size == SNDRV_TRIDENT_PAGE_SIZE */ 29 #define ALIGN_PAGE_SIZE PAGE_SIZE /* minimum page size for allocation */ 30 #define MAX_ALIGN_PAGES SNDRV_TRIDENT_MAX_PAGES /* maxmium aligned pages */ 31 /* fill TLB entrie(s) corresponding to page with ptr */ 32 #define set_tlb_bus(trident,page,addr) __set_tlb_bus(trident,page,addr) 33 /* fill TLB entrie(s) corresponding to page with silence pointer */ 34 #define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, trident->tlb.silent_page->addr) 35 /* get aligned page from offset address */ 36 #define get_aligned_page(offset) ((offset) >> 12) 37 /* get offset address from aligned page */ 38 #define aligned_page_offset(page) ((page) << 12) 39 /* get PCI physical address from aligned page */ 40 #define page_to_addr(trident,page) __tlb_to_addr(trident, page) 41 42 #elif PAGE_SIZE == 8192 43 /* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/ 44 #define ALIGN_PAGE_SIZE PAGE_SIZE 45 #define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / 2) 46 #define get_aligned_page(offset) ((offset) >> 13) 47 #define aligned_page_offset(page) ((page) << 13) 48 #define page_to_addr(trident,page) __tlb_to_addr(trident, (page) << 1) 49 50 /* fill TLB entries -- we need to fill two entries */ 51 static inline void set_tlb_bus(struct snd_trident *trident, int page, 52 dma_addr_t addr) 53 { 54 page <<= 1; 55 __set_tlb_bus(trident, page, addr); 56 __set_tlb_bus(trident, page+1, addr + SNDRV_TRIDENT_PAGE_SIZE); 57 } 58 static inline void set_silent_tlb(struct snd_trident *trident, int page) 59 { 60 page <<= 1; 61 __set_tlb_bus(trident, page, trident->tlb.silent_page->addr); 62 __set_tlb_bus(trident, page+1, trident->tlb.silent_page->addr); 63 } 64 65 #else 66 /* arbitrary size */ 67 #define UNIT_PAGES (PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE) 68 #define ALIGN_PAGE_SIZE (SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES) 69 #define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES) 70 /* Note: if alignment doesn't match to the maximum size, the last few blocks 71 * become unusable. To use such blocks, you'll need to check the validity 72 * of accessing page in set_tlb_bus and set_silent_tlb. search_empty() 73 * should also check it, too. 74 */ 75 #define get_aligned_page(offset) ((offset) / ALIGN_PAGE_SIZE) 76 #define aligned_page_offset(page) ((page) * ALIGN_PAGE_SIZE) 77 #define page_to_addr(trident,page) __tlb_to_addr(trident, (page) * UNIT_PAGES) 78 79 /* fill TLB entries -- UNIT_PAGES entries must be filled */ 80 static inline void set_tlb_bus(struct snd_trident *trident, int page, 81 dma_addr_t addr) 82 { 83 int i; 84 page *= UNIT_PAGES; 85 for (i = 0; i < UNIT_PAGES; i++, page++) { 86 __set_tlb_bus(trident, page, addr); 87 addr += SNDRV_TRIDENT_PAGE_SIZE; 88 } 89 } 90 static inline void set_silent_tlb(struct snd_trident *trident, int page) 91 { 92 int i; 93 page *= UNIT_PAGES; 94 for (i = 0; i < UNIT_PAGES; i++, page++) 95 __set_tlb_bus(trident, page, trident->tlb.silent_page->addr); 96 } 97 98 #endif /* PAGE_SIZE */ 99 100 /* first and last (aligned) pages of memory block */ 101 #define firstpg(blk) (((struct snd_trident_memblk_arg *)snd_util_memblk_argptr(blk))->first_page) 102 #define lastpg(blk) (((struct snd_trident_memblk_arg *)snd_util_memblk_argptr(blk))->last_page) 103 104 /* 105 * search empty pages which may contain given size 106 */ 107 static struct snd_util_memblk * 108 search_empty(struct snd_util_memhdr *hdr, int size) 109 { 110 struct snd_util_memblk *blk; 111 int page, psize; 112 struct list_head *p; 113 114 psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1); 115 page = 0; 116 list_for_each(p, &hdr->block) { 117 blk = list_entry(p, struct snd_util_memblk, list); 118 if (page + psize <= firstpg(blk)) 119 goto __found_pages; 120 page = lastpg(blk) + 1; 121 } 122 if (page + psize > MAX_ALIGN_PAGES) 123 return NULL; 124 125 __found_pages: 126 /* create a new memory block */ 127 blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev); 128 if (blk == NULL) 129 return NULL; 130 blk->offset = aligned_page_offset(page); /* set aligned offset */ 131 firstpg(blk) = page; 132 lastpg(blk) = page + psize - 1; 133 return blk; 134 } 135 136 137 /* 138 * check if the given pointer is valid for pages 139 */ 140 static int is_valid_page(struct snd_trident *trident, unsigned long ptr) 141 { 142 if (ptr & ~0x3fffffffUL) { 143 dev_err(trident->card->dev, "max memory size is 1GB!!\n"); 144 return 0; 145 } 146 if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) { 147 dev_err(trident->card->dev, "page is not aligned\n"); 148 return 0; 149 } 150 return 1; 151 } 152 153 /* 154 * page allocation for DMA (Scatter-Gather version) 155 */ 156 static struct snd_util_memblk * 157 snd_trident_alloc_sg_pages(struct snd_trident *trident, 158 struct snd_pcm_substream *substream) 159 { 160 struct snd_util_memhdr *hdr; 161 struct snd_util_memblk *blk; 162 struct snd_pcm_runtime *runtime = substream->runtime; 163 int idx, page; 164 165 if (snd_BUG_ON(runtime->dma_bytes <= 0 || 166 runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES * 167 SNDRV_TRIDENT_PAGE_SIZE)) 168 return NULL; 169 hdr = trident->tlb.memhdr; 170 if (snd_BUG_ON(!hdr)) 171 return NULL; 172 173 174 175 guard(mutex)(&hdr->block_mutex); 176 blk = search_empty(hdr, runtime->dma_bytes); 177 if (blk == NULL) 178 return NULL; 179 180 /* set TLB entries */ 181 idx = 0; 182 for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { 183 unsigned long ofs = idx << PAGE_SHIFT; 184 dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs); 185 if (!is_valid_page(trident, addr)) { 186 __snd_util_mem_free(hdr, blk); 187 return NULL; 188 } 189 set_tlb_bus(trident, page, addr); 190 } 191 return blk; 192 } 193 194 /* 195 * page allocation for DMA (contiguous version) 196 */ 197 static struct snd_util_memblk * 198 snd_trident_alloc_cont_pages(struct snd_trident *trident, 199 struct snd_pcm_substream *substream) 200 { 201 struct snd_util_memhdr *hdr; 202 struct snd_util_memblk *blk; 203 int page; 204 struct snd_pcm_runtime *runtime = substream->runtime; 205 dma_addr_t addr; 206 207 if (snd_BUG_ON(runtime->dma_bytes <= 0 || 208 runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES * 209 SNDRV_TRIDENT_PAGE_SIZE)) 210 return NULL; 211 hdr = trident->tlb.memhdr; 212 if (snd_BUG_ON(!hdr)) 213 return NULL; 214 215 guard(mutex)(&hdr->block_mutex); 216 blk = search_empty(hdr, runtime->dma_bytes); 217 if (blk == NULL) 218 return NULL; 219 220 /* set TLB entries */ 221 addr = runtime->dma_addr; 222 for (page = firstpg(blk); page <= lastpg(blk); page++, 223 addr += SNDRV_TRIDENT_PAGE_SIZE) { 224 if (!is_valid_page(trident, addr)) { 225 __snd_util_mem_free(hdr, blk); 226 return NULL; 227 } 228 set_tlb_bus(trident, page, addr); 229 } 230 return blk; 231 } 232 233 /* 234 * page allocation for DMA 235 */ 236 struct snd_util_memblk * 237 snd_trident_alloc_pages(struct snd_trident *trident, 238 struct snd_pcm_substream *substream) 239 { 240 if (snd_BUG_ON(!trident || !substream)) 241 return NULL; 242 if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG) 243 return snd_trident_alloc_sg_pages(trident, substream); 244 else 245 return snd_trident_alloc_cont_pages(trident, substream); 246 } 247 248 249 /* 250 * release DMA buffer from page table 251 */ 252 int snd_trident_free_pages(struct snd_trident *trident, 253 struct snd_util_memblk *blk) 254 { 255 struct snd_util_memhdr *hdr; 256 int page; 257 258 if (snd_BUG_ON(!trident || !blk)) 259 return -EINVAL; 260 261 hdr = trident->tlb.memhdr; 262 guard(mutex)(&hdr->block_mutex); 263 /* reset TLB entries */ 264 for (page = firstpg(blk); page <= lastpg(blk); page++) 265 set_silent_tlb(trident, page); 266 /* free memory block */ 267 __snd_util_mem_free(hdr, blk); 268 return 0; 269 } 270