dsp_mem.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,475 行 · 第 1/3 页
C
1,475 行
cam_l_va_mask = get_cam_l_va_mask(slst); cam_va = (unsigned long)(cam_h & DSPMMU_CAM_H_VA_TAG_H_MASK) << 22 | (unsigned long)(cam_l & cam_l_va_mask) << 6; if (cam_va == vadr) /* flush the entry */ dspmmu_flush(); else max_valid = i; } /* set new lock base */ set_tlb_lock(max_valid+1, max_valid+1); clk_unuse(dsp_ck_handle); return 0;}static void omap_dsp_dspmmu_gflush(void){ clk_use(dsp_ck_handle); __dspmmu_gflush(); set_tlb_lock(1, 1); clk_unuse(dsp_ck_handle);}/* * omap_dsp_exmap() * * OMAP_DSP_MEM_IOCTL_EXMAP ioctl calls this function with padr=0. * In this case, the buffer for DSP is allocated in this routine, * then it is mapped. * On the other hand, for example - frame buffer sharing, calls * this function with padr set. It means some known address space * pointed with padr is going to be shared with DSP. */static int omap_dsp_exmap(unsigned long dspadr, unsigned long padr, unsigned long size){ unsigned short slst; void *buf; unsigned int order = 0; unsigned long unit; unsigned int cntnu = 0; unsigned long _dspadr = dspadr; unsigned long _padr = padr; void *_vadr = dspbyte_to_virt(dspadr); unsigned long _size = size; struct exmap *exmap_ent; int status; int i;#define MINIMUM_PAGESZ SZ_4KB /* * alignment check */ if (!is_aligned(size, MINIMUM_PAGESZ)) { printk(KERN_ERR "omapdsp: size(0x%lx) is " "not multiple of 4KB.\n", size); return -EINVAL; } if (!is_aligned(dspadr, MINIMUM_PAGESZ)) { printk(KERN_ERR "omapdsp: DSP address(0x%lx) is " "not aligned.\n", dspadr); return -EINVAL; } if (!is_aligned(padr, MINIMUM_PAGESZ)) { printk(KERN_ERR "omapdsp: physical address(0x%lx) is " "not aligned.\n", padr); return -EINVAL; } /* address validity check */ if ((dspadr < dspmem_size) || ((dspadr + size > DSP_INIT_PAGE) && (dspadr < DSP_INIT_PAGE + PAGE_SIZE))) { printk(KERN_ERR "omapdsp: illegal address/size " "for omap_dsp_exmap().\n"); return -EINVAL; } /* overlap check */ for (i = 0; i < DSPMMU_TLB_LINES; i++) { unsigned long mapsize; struct exmap *tmp_ent = &exmap[i]; if (!tmp_ent->valid) continue; mapsize = 1 << (tmp_ent->order + PAGE_SHIFT); if ((_vadr + size > tmp_ent->vadr) && (_vadr < tmp_ent->vadr + mapsize)) { printk(KERN_ERR "omapdsp: exmap page overlap!\n"); return -EINVAL; } }start: buf = NULL; /* Are there any free TLB lines? */ for (i = 0; i < DSPMMU_TLB_LINES; i++) { if (!exmap[i].valid) goto found_free; } printk(KERN_ERR "omapdsp: DSP TLB is full.\n"); status = -EBUSY; goto fail;found_free: exmap_ent = &exmap[i]; if ((_size >= SZ_1MB) && (is_aligned(_padr, SZ_1MB) || (padr == 0)) && is_aligned(_dspadr, SZ_1MB)) { unit = SZ_1MB; slst = DSPMMU_CAM_L_SLST_1MB; order = 20 - PAGE_SHIFT; } else if ((_size >= SZ_64KB) && (is_aligned(_padr, SZ_64KB) || (padr == 0)) && is_aligned(_dspadr, SZ_64KB)) { unit = SZ_64KB; slst = DSPMMU_CAM_L_SLST_64KB; order = 16 - PAGE_SHIFT; } else /* if (_size >= SZ_4KB) */ { unit = SZ_4KB; slst = DSPMMU_CAM_L_SLST_4KB; order = 12 - PAGE_SHIFT; }#if 0 /* 1KB is not enabled */ else if (_size >= SZ_1KB) { unit = SZ_1KB; slst = DSPMMU_CAM_L_SLST_1KB; order = 10 - PAGE_SHIFT; }#endif /* buffer allocation */ if (padr == 0) { struct page *page, *ps, *pe; buf = (void *)__get_free_pages(GFP_KERNEL, order); if (buf == NULL) { status = -ENOMEM; goto fail; } /* mark the pages as reserved; this is needed for mmap */ ps = virt_to_page(buf); pe = virt_to_page(buf + unit); for (page = ps; page < pe; page++) { SetPageReserved(page); } _padr = __pa(buf); } /* * mapping for ARM MMU: * we should not access to the allocated memory through 'buf' * since this area should not be cashed. */ status = omap_dsp_exmap_set_armmmu((unsigned long)_vadr, _padr, unit); if (status < 0) goto fail; /* loading DSP TLB entry */ status = omap_dsp_load_static_tlb(_dspadr, _padr, slst, 0, DSPMMU_RAM_L_AP_FA); if (status < 0) { omap_dsp_exmap_clear_armmmu((unsigned long)_vadr, unit); goto fail; } exmap_ent->buf = buf; exmap_ent->vadr = _vadr; exmap_ent->order = order; exmap_ent->valid = 1; exmap_ent->cntnu = cntnu; if ((_size -= unit) == 0) return size; _dspadr += unit; _vadr += unit; _padr = padr ? _padr + unit : 0; cntnu = 1; goto start;fail: if (buf) free_pages((unsigned long)buf, order); omap_dsp_exunmap(dspadr); return status;}static int omap_dsp_exunmap(unsigned long dspadr){ void *vadr; unsigned long size, total = 0; struct exmap *exmap_ent; int idx; vadr = dspbyte_to_virt(dspadr); for (idx = 0; idx < DSPMMU_TLB_LINES; idx++) { if (!exmap[idx].valid) continue; if (exmap[idx].vadr == vadr) goto found_map; } printk(KERN_WARNING "omapdsp: address %06lx not found in exmap table.\n", dspadr); return -EINVAL;found_map: preempt_disable(); exmap_ent = &exmap[idx]; /* clearing DSP TLB entry */ omap_dsp_clear_static_tlb(dspadr); /* clearning ARM MMU */ size = 1 << (exmap_ent->order + PAGE_SHIFT); omap_dsp_exmap_clear_armmmu((unsigned long)vadr, size); /* freeing allocated memory */ if (exmap_ent->buf) { printk(KERN_DEBUG "omapdsp: freeing 0x%lx bytes @ adr 0x%8p\n", size, exmap_ent->buf); free_pages((unsigned long)exmap_ent->buf, exmap_ent->order); } /* we don't free PTEs */ /* flush TLB */ flush_tlb_kernel_range((unsigned long)vadr, (unsigned long)vadr + size); /* * we should clear processes' mm as well, * because processes might had accessed to those spaces * with old table in the past. */ omap_dsp_cur_users_map_update(); preempt_enable(); exmap_ent->valid = 0; total += size; /* check if next mapping is in same group */ idx++; if ((idx < DSPMMU_TLB_LINES) && exmap[idx].valid && exmap[idx].cntnu) { dspadr += size; vadr += size; if (exmap[idx].vadr != vadr) { printk(KERN_ERR "omapdsp: illegal exmap grouping!\n" "expected vadr = %p, " "exmap[%d].vadr = %p\n", vadr, idx, exmap[idx].vadr); return -EINVAL; } goto found_map; } return total;}static void omap_dsp_exmap_flush(void){ pmd_t *pmdp, *s_pmd, *e_pmd, *initcode_pmd; unsigned long virt; int i; preempt_disable(); /* clearing DSP TLB entry */ omap_dsp_dspmmu_gflush(); /* exmap[0] should be preserved */ for (i = 1; i < DSPMMU_TLB_LINES; i++) { unsigned long size; if (!exmap[i].valid) continue; /* clearing ARM MMU */ size = 1 << (exmap[i].order + PAGE_SHIFT); omap_dsp_exmap_clear_armmmu((unsigned long)exmap[i].vadr, size); /* freeing allocated memory */ free_pages((unsigned long)exmap[i].buf, exmap[i].order); printk(KERN_DEBUG "omapdsp: freeing 0x%lx bytes @ adr 0x%8p\n", size, exmap[i].buf); exmap[i].valid = 0; } /* freeing PTE */ virt = PMD2_ALIGN(dspmem_base + dspmem_size); s_pmd = pmd_offset(pgd_offset_k(virt), virt); virt = PMD2_ALIGN(dspmem_base + DSPSPACE_SIZE); e_pmd = pmd_offset(pgd_offset_k(virt), virt); virt = (unsigned long)dspbyte_to_virt(DSP_INIT_PAGE); /* 0xe0e00000 */ initcode_pmd = pmd_offset(pgd_offset_k(virt), virt); for (pmdp = s_pmd; pmdp < e_pmd; pmdp += 2) { /* do not free the PTE for DSP vector table */ if (pmdp == initcode_pmd) continue; if (!pmd_none(*pmdp)) { pte_t *pte = pte_offset_kernel(pmdp, 0); /* note: clears two PMDs */ pmd_clear(pmdp); pte_free_kernel(pte); } } /* flush TLB */ flush_tlb_kernel_range(dspmem_base + dspmem_size, dspmem_base + DSPSPACE_SIZE); /* * we should clear processes' mm as well, * because processes might had accessed to those spaces * with old table in the past. */ omap_dsp_cur_users_map_update(); preempt_enable();}#ifdef CONFIG_OMAP_DSP_FBEXPORT#ifndef CONFIG_FB#error You configured OMAP_DSP_FBEXPORT, but FB was not configured!#endif /* CONFIG_FB */static int omap_dsp_fbexport(unsigned long *dspadr){ unsigned long dspadr_actual; unsigned long padr; unsigned long fbsz; int cnt; printk(KERN_DEBUG "omapdsp: frame buffer export\n"); if (num_registered_fb == 0) { printk(KERN_INFO "omapdsp: frame buffer not registered.\n"); return -EINVAL; } if (num_registered_fb != 1) { printk(KERN_INFO "omapdsp: %d frame buffers found. " "we use first one.\n", num_registered_fb); } padr = registered_fb[0]->fix.smem_start; fbsz = registered_fb[0]->fix.smem_len; /* * The exporting framebuffer driver should consider * DSP TLB consumption. * (64kB alignment is desirable for both address and size.) */ if ((padr & (SZ_4KB-1)) || (fbsz & (SZ_4KB-1))) { printk(KERN_ERR "omapdsp: framebuffer base address or size " "is not aligned in 4kB:\n" " adr = %08lx, size = %08lx\n", padr, fbsz); return -EINVAL; } /* line up dspadr offset with padr */ dspadr_actual = (fbsz > SZ_1MB) ? lineup_offset(*dspadr, padr, SZ_1MB-1) : (fbsz > SZ_64KB) ? lineup_offset(*dspadr, padr, SZ_64KB-1) : /* (fbsz > SZ_4KB) ? */ *dspadr; if (dspadr_actual != *dspadr) printk(KERN_DEBUG "omapdsp: actual dspadr for FBEXPORT = %08lx\n", dspadr_actual); *dspadr = dspadr_actual; cnt = omap_dsp_exmap(dspadr_actual, padr, fbsz); if (cnt < 0) { printk(KERN_ERR "omapdsp: exmap failure.\n"); return cnt; } /* * increase the DMA priority * makoto.sugano@nokia.com */ set_emiff_dma_prio(15); return cnt;}#else /* CONFIG_OMAP_DSP_FBEXPORT */static int omap_dsp_fbexport(unsigned long *dspadr){ printk(KERN_ERR "omapdsp: FBEXPORT function is not enabled.\n"); return -EINVAL;}#endif /* CONFIG_OMAP_DSP_FBEXPORT */static int omap_dsp_mmu_itack(void){ unsigned long dspadr; printk(KERN_INFO "omapdsp: sending DSP MMU interrupt ack.\n"); if (!omap_dsp_err_mmu_isset()) { printk(KERN_ERR "omapdsp: DSP MMU error has not been set.\n"); return -EINVAL; } dspadr = dsp_fault_adr & ~(SZ_4K-1); omap_dsp_exmap(dspadr, 0, SZ_4K); /* FIXME: reserve TLB entry for this */ printk(KERN_INFO "omapdsp: falling into recovery runlevel...\n"); omap_dsp_runlevel(OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY); __dspmmu_itack(); udelay(100); omap_dsp_exunmap(dspadr); omap_dsp_err_mmu_clear(); return 0;}static void omap_dsp_mmu_init(void){ unsigned long phys; void *virt; clk_use(dsp_ck_handle); dspmmu_disable(); /* clear all */ udelay(10); dspmmu_enable(); /* mapping for ARM MMU */ phys = __pa(dspvect_page); virt = dspbyte_to_virt(DSP_INIT_PAGE); /* 0xe0fff000 */ omap_dsp_exmap_set_armmmu((unsigned long)virt, phys, PAGE_SIZE); exmap[0].buf = dspvect_page; exmap[0].vadr = virt; exmap[0].order = 0; exmap[0].valid = 1; exmap[0].cntnu = 0; /* DSP TLB initialization */ set_tlb_lock(0, 0); omap_dsp_load_static_tlb(DSP_INIT_PAGE, phys, DSPMMU_CAM_L_SLST_4KB, DSPMMU_CAM_L_P, /* preserved */ DSPMMU_RAM_L_AP_FA); /* full access */ clk_unuse(dsp_ck_handle);}void omap_dsp_mmu_shutdown(void){ omap_dsp_exmap_flush(); if (dspvect_page != NULL) { unsigned long virt; pmd_t *pmdp; pte_t *ptep; free_page((unsigned long)dspvect_page); dspvect_page = NULL; virt = (unsigned long)dspbyte_to_virt(DSP_INIT_PAGE); pmdp = pmd_offset(pgd_offset_k(virt), virt); ptep = pte_offset_kernel(pmdp, 0); pmd_clear(pmdp); pte_free_kernel(ptep); } dspmmu_disable(); /* clear all */}/* * */int omap_dsp_enable_dspmem(void){ if (omap_dsp_is_config_done()) return omap_dsp_mbsend(MBCMD(PM), OMAP_DSP_MBCMD_PM_ENABLE, DSPREG_ICR_DMA_IDLE_DOMAIN);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?