trident_memory.c (8145B)
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 */ 51static 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} 58static 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 */ 80static 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} 90static 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 */ 107static struct snd_util_memblk * 108search_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 */ 140static int is_valid_page(unsigned long ptr) 141{ 142 if (ptr & ~0x3fffffffUL) { 143 snd_printk(KERN_ERR "max memory size is 1GB!!\n"); 144 return 0; 145 } 146 if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) { 147 snd_printk(KERN_ERR "page is not aligned\n"); 148 return 0; 149 } 150 return 1; 151} 152 153/* 154 * page allocation for DMA (Scatter-Gather version) 155 */ 156static struct snd_util_memblk * 157snd_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 mutex_lock(&hdr->block_mutex); 176 blk = search_empty(hdr, runtime->dma_bytes); 177 if (blk == NULL) { 178 mutex_unlock(&hdr->block_mutex); 179 return NULL; 180 } 181 182 /* set TLB entries */ 183 idx = 0; 184 for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { 185 unsigned long ofs = idx << PAGE_SHIFT; 186 dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs); 187 if (! is_valid_page(addr)) { 188 __snd_util_mem_free(hdr, blk); 189 mutex_unlock(&hdr->block_mutex); 190 return NULL; 191 } 192 set_tlb_bus(trident, page, addr); 193 } 194 mutex_unlock(&hdr->block_mutex); 195 return blk; 196} 197 198/* 199 * page allocation for DMA (contiguous version) 200 */ 201static struct snd_util_memblk * 202snd_trident_alloc_cont_pages(struct snd_trident *trident, 203 struct snd_pcm_substream *substream) 204{ 205 struct snd_util_memhdr *hdr; 206 struct snd_util_memblk *blk; 207 int page; 208 struct snd_pcm_runtime *runtime = substream->runtime; 209 dma_addr_t addr; 210 211 if (snd_BUG_ON(runtime->dma_bytes <= 0 || 212 runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES * 213 SNDRV_TRIDENT_PAGE_SIZE)) 214 return NULL; 215 hdr = trident->tlb.memhdr; 216 if (snd_BUG_ON(!hdr)) 217 return NULL; 218 219 mutex_lock(&hdr->block_mutex); 220 blk = search_empty(hdr, runtime->dma_bytes); 221 if (blk == NULL) { 222 mutex_unlock(&hdr->block_mutex); 223 return NULL; 224 } 225 226 /* set TLB entries */ 227 addr = runtime->dma_addr; 228 for (page = firstpg(blk); page <= lastpg(blk); page++, 229 addr += SNDRV_TRIDENT_PAGE_SIZE) { 230 if (! is_valid_page(addr)) { 231 __snd_util_mem_free(hdr, blk); 232 mutex_unlock(&hdr->block_mutex); 233 return NULL; 234 } 235 set_tlb_bus(trident, page, addr); 236 } 237 mutex_unlock(&hdr->block_mutex); 238 return blk; 239} 240 241/* 242 * page allocation for DMA 243 */ 244struct snd_util_memblk * 245snd_trident_alloc_pages(struct snd_trident *trident, 246 struct snd_pcm_substream *substream) 247{ 248 if (snd_BUG_ON(!trident || !substream)) 249 return NULL; 250 if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG) 251 return snd_trident_alloc_sg_pages(trident, substream); 252 else 253 return snd_trident_alloc_cont_pages(trident, substream); 254} 255 256 257/* 258 * release DMA buffer from page table 259 */ 260int snd_trident_free_pages(struct snd_trident *trident, 261 struct snd_util_memblk *blk) 262{ 263 struct snd_util_memhdr *hdr; 264 int page; 265 266 if (snd_BUG_ON(!trident || !blk)) 267 return -EINVAL; 268 269 hdr = trident->tlb.memhdr; 270 mutex_lock(&hdr->block_mutex); 271 /* reset TLB entries */ 272 for (page = firstpg(blk); page <= lastpg(blk); page++) 273 set_silent_tlb(trident, page); 274 /* free memory block */ 275 __snd_util_mem_free(hdr, blk); 276 mutex_unlock(&hdr->block_mutex); 277 return 0; 278}