/* * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 * Copyright (c) 2008-2009 QUALCOMM USA, INC. * * All source code in this file is licensed under the following license * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you can find it at http://www.fsf.org */ #include #include #include #include "kgsl_sharedmem.h" #include "kgsl_device.h" #include "kgsl.h" #include "kgsl_log.h" /* block alignment shift count */ static inline unsigned int kgsl_memarena_get_order(uint32_t flags) { unsigned int alignshift; alignshift = ((flags & KGSL_MEMFLAGS_ALIGN_MASK) >> KGSL_MEMFLAGS_ALIGN_SHIFT); return alignshift; } /* block alignment shift count */ static inline unsigned int kgsl_memarena_align(unsigned int address, unsigned int shift) { unsigned int alignedbaseaddr = ((address) >> shift) << shift; if (alignedbaseaddr < address) alignedbaseaddr += (1 << shift); return alignedbaseaddr; } int kgsl_sharedmem_init(struct kgsl_sharedmem *shmem) { int result = -EINVAL; if (!request_mem_region(shmem->physbase, shmem->size, DRIVER_NAME)) { KGSL_MEM_ERR("request_mem_region failed\n"); goto error; } shmem->baseptr = ioremap(shmem->physbase, shmem->size); KGSL_MEM_INFO("ioremap(shm) = %p\n", shmem->baseptr); if (shmem->baseptr == NULL) { KGSL_MEM_ERR("ioremap failed for address %08x size %d\n", shmem->physbase, shmem->size); result = -ENODEV; goto error_release_mem; } shmem->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1); if (shmem->pool == NULL) { KGSL_MEM_ERR("gen_pool_create failed\n"); result = -ENOMEM; goto error_iounmap; } if (gen_pool_add(shmem->pool, shmem->physbase, shmem->size, -1)) { KGSL_MEM_ERR("gen_pool_create failed\n"); result = -ENOMEM; goto error_pool_destroy; } result = 0; KGSL_MEM_INFO("physbase 0x%08x size 0x%08x baseptr 0x%p\n", shmem->physbase, shmem->size, shmem->baseptr); return 0; error_pool_destroy: gen_pool_destroy(shmem->pool); error_iounmap: iounmap(shmem->baseptr); shmem->baseptr = NULL; error_release_mem: release_mem_region(shmem->physbase, shmem->size); error: return result; } int kgsl_sharedmem_close(struct kgsl_sharedmem *shmem) { if (shmem->pool) { gen_pool_destroy(shmem->pool); shmem->pool = NULL; } if (shmem->baseptr != NULL) { KGSL_MEM_INFO("iounmap(shm) = %p\n", shmem->baseptr); iounmap(shmem->baseptr); shmem->baseptr = NULL; release_mem_region(shmem->physbase, shmem->size); } return 0; } /* * get the host mapped address for a hardware device address */ static void *kgsl_memarena_gethostptr(struct kgsl_sharedmem *shmem, uint32_t physaddr) { void *result; KGSL_MEM_VDBG("enter (memarena=%p, physaddr=0x%08x)\n", shmem, physaddr); BUG_ON(shmem == NULL); /* check address range */ if (physaddr < shmem->physbase) return NULL; if (physaddr >= shmem->physbase + shmem->size) return NULL; if (shmem->baseptr == NULL) { KGSL_MEM_VDBG("return: %p\n", NULL); return NULL; } result = ((physaddr - shmem->physbase) + shmem->baseptr); KGSL_MEM_VDBG("return: %p\n", result); return result; } int kgsl_sharedmem_alloc(uint32_t flags, int size, struct kgsl_memdesc *memdesc) { struct kgsl_sharedmem *shmem; int result = -ENOMEM; unsigned int blksize; unsigned int baseaddr; unsigned int alignshift; unsigned int alignedbaseaddr; KGSL_MEM_VDBG("enter (flags=0x%08x, size=%d, memdesc=%p)\n", flags, size, memdesc); shmem = &kgsl_driver.shmem; BUG_ON(memdesc == NULL); BUG_ON(size <= 0); alignshift = kgsl_memarena_get_order(flags); size = ALIGN(size, KGSL_PAGESIZE); blksize = size; if (alignshift > KGSL_PAGESIZE_SHIFT) blksize += (1 << alignshift) - KGSL_PAGESIZE; baseaddr = gen_pool_alloc(shmem->pool, blksize); if (baseaddr == 0) { KGSL_MEM_ERR("gen_pool_alloc failed\n"); result = -ENOMEM; goto done; } result = 0; if (alignshift > KGSL_PAGESIZE_SHIFT) { alignedbaseaddr = ALIGN(baseaddr, (1 << alignshift)); KGSL_MEM_VDBG("ba %x al %x as %d m->as %d bs %x s %x\n", baseaddr, alignedbaseaddr, alignshift, KGSL_PAGESIZE_SHIFT, blksize, size); if (alignedbaseaddr > baseaddr) { KGSL_MEM_VDBG("physaddr %x free before %x size %x\n", alignedbaseaddr, baseaddr, alignedbaseaddr - baseaddr); gen_pool_free(shmem->pool, baseaddr, alignedbaseaddr - baseaddr); blksize -= alignedbaseaddr - baseaddr; } if (blksize > size) { KGSL_MEM_VDBG("physaddr %x free after %x size %x\n", alignedbaseaddr, alignedbaseaddr + size, blksize - size); gen_pool_free(shmem->pool, alignedbaseaddr + size, blksize - size); } } else { alignedbaseaddr = baseaddr; } memdesc->physaddr = alignedbaseaddr; memdesc->hostptr = kgsl_memarena_gethostptr(shmem, memdesc->physaddr); memdesc->size = size; KGSL_MEM_VDBG("ashift %d m->ashift %d blksize %d base %x abase %x\n", alignshift, KGSL_PAGESIZE_SHIFT, blksize, baseaddr, alignedbaseaddr); done: if (result) memset(memdesc, 0, sizeof(*memdesc)); KGSL_MEM_VDBG("return: %d\n", result); return result; } void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) { struct kgsl_sharedmem *shmem = &kgsl_driver.shmem; KGSL_MEM_VDBG("enter (shmem=%p, memdesc=%p, physaddr=%08x, size=%d)\n", shmem, memdesc, memdesc->physaddr, memdesc->size); BUG_ON(memdesc == NULL); BUG_ON(memdesc->size <= 0); BUG_ON(shmem->physbase > memdesc->physaddr); BUG_ON((shmem->physbase + shmem->size) < (memdesc->physaddr + memdesc->size)); gen_pool_free(shmem->pool, memdesc->physaddr, memdesc->size); memset(memdesc, 0, sizeof(struct kgsl_memdesc)); KGSL_MEM_VDBG("return\n"); } int kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, unsigned int offsetbytes, unsigned int sizebytes) { if (memdesc == NULL || memdesc->hostptr == NULL || dst == NULL) { KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p dst %p\n", memdesc, (memdesc ? memdesc->hostptr : NULL), dst); return -EINVAL; } if (offsetbytes + sizebytes > memdesc->size) { KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", offsetbytes, sizebytes, memdesc->size); return -ERANGE; } memcpy(dst, memdesc->hostptr + offsetbytes, sizebytes); return 0; } int kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, void *value, unsigned int sizebytes) { if (memdesc == NULL || memdesc->hostptr == NULL) { KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc, (memdesc ? memdesc->hostptr : NULL)); return -EINVAL; } if (offsetbytes + sizebytes > memdesc->size) { KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", offsetbytes, sizebytes, memdesc->size); return -ERANGE; } memcpy(memdesc->hostptr + offsetbytes, value, sizebytes); return 0; } int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, unsigned int value, unsigned int sizebytes) { if (memdesc == NULL || memdesc->hostptr == NULL) { KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc, (memdesc ? memdesc->hostptr : NULL)); return -EINVAL; } if (offsetbytes + sizebytes > memdesc->size) { KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", offsetbytes, sizebytes, memdesc->size); return -ERANGE; } memset(memdesc->hostptr + offsetbytes, value, sizebytes); return 0; }