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 + -
显示快捷键?