diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index 7ffc8b50..b7c40a1e 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -395,6 +395,16 @@ int kgsl_mmu_init(struct kgsl_device *device) return -ENOMEM; } mmu->hwpagetable = mmu->defaultpagetable; + mmu->tlbflushfilter.size = (mmu->va_range / + (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1; + mmu->tlbflushfilter.base = (unsigned int *) + kzalloc(mmu->tlbflushfilter.size, GFP_KERNEL); + if (!mmu->tlbflushfilter.base) { + KGSL_MEM_ERR("Failed to create tlbflushfilter\n"); + kgsl_mmu_close(device); + return -ENOMEM; + } + GSL_TLBFLUSH_FILTER_RESET(); kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, mmu->hwpagetable->base.gpuaddr); kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE, @@ -455,7 +465,7 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, unsigned int flags) { int numpages; - unsigned int pte, superpte, ptefirst, ptelast, physaddr; + unsigned int pte, ptefirst, ptelast, physaddr; int flushtlb, alloc_size; struct kgsl_mmu *mmu = NULL; int phys_contiguous = flags & KGSL_MEMFLAGS_CONPHYS; @@ -516,15 +526,11 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, pte = ptefirst; flushtlb = 0; - superpte = ptefirst & (GSL_PT_SUPER_PTE - 1); - for (pte = superpte; pte < ptefirst; pte++) { - /* tlb needs to be flushed only when a dirty superPTE - gets backed */ - if (kgsl_pt_map_isdirty(pagetable, pte)) { - flushtlb = 1; - break; - } - } + /* tlb needs to be flushed when the first and last pte are not at + * superpte boundaries */ + if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || + ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) + flushtlb = 1; for (pte = ptefirst; pte < ptelast; pte++) { #ifdef VERBOSE_DEBUG @@ -532,8 +538,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, uint32_t val = kgsl_pt_map_getaddr(pagetable, pte); BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY); #endif - if (kgsl_pt_map_isdirty(pagetable, pte)) - flushtlb = 1; + if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) + if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) + flushtlb = 1; + /* mark pte as in use */ if (phys_contiguous) physaddr = address; @@ -554,17 +562,6 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, address += KGSL_PAGESIZE; } - /* set superpte to end of next superpte */ - superpte = (ptelast + (GSL_PT_SUPER_PTE - 1)) - & (GSL_PT_SUPER_PTE - 1); - for (pte = ptelast; pte < superpte; pte++) { - /* tlb needs to be flushed only when a dirty superPTE - gets backed */ - if (kgsl_pt_map_isdirty(pagetable, pte)) { - flushtlb = 1; - break; - } - } KGSL_MEM_INFO("pt %p p %08x g %08x pte f %d l %d n %d f %d\n", pagetable, address, *gpuaddr, ptefirst, ptelast, numpages, flushtlb); @@ -573,8 +570,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, /* Invalidate tlb only if current page table used by GPU is the * pagetable that we used to allocate */ - if (pagetable == mmu->hwpagetable) + if (flushtlb && (pagetable == mmu->hwpagetable)) { kgsl_yamato_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH); + GSL_TLBFLUSH_FILTER_RESET(); + } KGSL_MEM_VDBG("return %d\n", 0); @@ -587,7 +586,8 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, int range) { unsigned int numpages; - unsigned int pte, ptefirst, ptelast; + unsigned int pte, ptefirst, ptelast, superpte; + struct kgsl_mmu *mmu = NULL; KGSL_MEM_VDBG("enter (pt=%p, gpuaddr=0x%08x, range=%d)\n", pagetable, gpuaddr, range); @@ -604,22 +604,24 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, KGSL_MEM_INFO("pt %p gpu %08x pte first %d last %d numpages %d\n", pagetable, gpuaddr, ptefirst, ptelast, numpages); + mmu = pagetable->mmu; + + superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1)); + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE); for (pte = ptefirst; pte < ptelast; pte++) { #ifdef VERBOSE_DEBUG /* check if PTE exists */ BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte)); #endif kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); + superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1)); + if (pte == superpte) + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / + GSL_PT_SUPER_PTE); } dmb(); - /* Invalidate tlb only if current page table used by GPU is the - * pagetable that we used to allocate */ - if (pagetable == pagetable->mmu->hwpagetable) - kgsl_yamato_setstate(pagetable->mmu->device, - KGSL_MMUFLAGS_TLBFLUSH); - gen_pool_free(pagetable->pool, gpuaddr, range); KGSL_MEM_VDBG("return %d\n", 0); @@ -653,6 +655,12 @@ int kgsl_mmu_close(struct kgsl_device *device) if (mmu->dummyspace.gpuaddr) kgsl_sharedmem_free(&mmu->dummyspace); + if (mmu->tlbflushfilter.base) { + mmu->tlbflushfilter.size = 0; + kfree(mmu->tlbflushfilter.base); + mmu->tlbflushfilter.base = NULL; + } + mmu->flags &= ~KGSL_FLAGS_STARTED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED0; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h index d70f24a1..60745187 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h @@ -31,6 +31,21 @@ #define KGSL_MMUFLAGS_TLBFLUSH 0x10000000 #define KGSL_MMUFLAGS_PTUPDATE 0x20000000 +/* Macros to manage TLB flushing */ +#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8) +#define GSL_TLBFLUSH_FILTER_GET(superpte) \ + (*((unsigned char *) \ + (((unsigned int)mmu->tlbflushfilter.base) \ + + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))) +#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \ + (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)) +#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) & \ + (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))) +#define GSL_TLBFLUSH_FILTER_RESET() memset(mmu->tlbflushfilter.base,\ + 0, mmu->tlbflushfilter.size) + extern unsigned int kgsl_cache_enable; struct kgsl_device; @@ -68,6 +83,11 @@ struct kgsl_pagetable { struct gen_pool *pool; }; +struct kgsl_tlbflushfilter { + unsigned int *base; + unsigned int size; +}; + struct kgsl_mmu { unsigned int refcnt; uint32_t flags; @@ -81,6 +101,8 @@ struct kgsl_mmu { /* current page table object being used by device mmu */ struct kgsl_pagetable *defaultpagetable; struct kgsl_pagetable *hwpagetable; + /* Maintain filter to manage tlb flushing */ + struct kgsl_tlbflushfilter tlbflushfilter; };