// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include "m4u_priv.h" #include "m4u_mva.h" static short mvaGraph[MVA_MAX_BLOCK_NR + 1]; static void *mvaInfoGraph[MVA_MAX_BLOCK_NR + 1]; static DEFINE_SPINLOCK(gMvaGraph_lock); void m4u_mvaGraph_init(void *priv_reserve) { int i; unsigned int vpu_reset_block_start = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_START); unsigned int vpu_reset_block_end = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_END); unsigned int vpu_fix_block_start = MVAGRAPH_INDEX(VPU_FIX_MVA_START); unsigned int vpu_fix_block_end = MVAGRAPH_INDEX(VPU_FIX_MVA_END); unsigned int ccu_fix_block_start = MVAGRAPH_INDEX(CCU_FIX_MVA_START); unsigned int ccu_fix_block_end = MVAGRAPH_INDEX(CCU_FIX_MVA_END); unsigned int ccu_nr; unsigned long irq_flags; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); memset(mvaGraph, 0, sizeof(short) * (MVA_MAX_BLOCK_NR + 1)); memset(mvaInfoGraph, 0, sizeof(void *) * (MVA_MAX_BLOCK_NR + 1)); mvaGraph[0] = (short)(1 | MVA_BUSY_MASK); mvaInfoGraph[0] = priv_reserve; /*need to reserve two mva block region for vpu. *[1,0x200],[0x360,0x500): shared with all ports. *[0x500, 0x501): reserved for vpu reset vector. *[0x501, 0x600): shared with all ports. *[0x600, 0x7E0): reserved for vpu. *[0x7E0, 0xFFF]: shared with all ports. */ /*[0x1,0x200],[0x360, 0x500]*/ mvaGraph[1] = GET_RANGE_SIZE(1, (ccu_fix_block_start - 1)); mvaGraph[ccu_fix_block_start - 1] = GET_RANGE_SIZE(1, (ccu_fix_block_start - 1)); mvaInfoGraph[1] = priv_reserve; mvaInfoGraph[ccu_fix_block_start - 1] = priv_reserve; M4UINFO("[0x1, 0x200): start: 1 end: 0x%x mvaGraph: 0x%x\n", (ccu_fix_block_start - 1), mvaGraph[1]); /*CCU:[0x200,0x360]*/ ccu_nr = MVA_GRAPH_BLOCK_NR_ALIGNED( CCU_FIX_MVA_END - CCU_FIX_MVA_START + 1); for (i = 0; i < ccu_nr; i++) MVA_SET_RESERVED(ccu_fix_block_start + i); mvaGraph[ccu_fix_block_start] = MVA_RESERVED_MASK | ccu_nr; mvaGraph[ccu_fix_block_end] = MVA_RESERVED_MASK | ccu_nr; mvaInfoGraph[ccu_fix_block_start] = priv_reserve; mvaInfoGraph[ccu_fix_block_end] = priv_reserve; M4UINFO("%s,start:0x%x,end:0x%x,mvaGraph:0x%x\n", __func__, ccu_fix_block_start, ccu_fix_block_end, mvaGraph[ccu_fix_block_start]); mvaGraph[ccu_fix_block_end + 1] = GET_RANGE_SIZE((ccu_fix_block_end + 1), (vpu_reset_block_start - 1)); mvaGraph[vpu_reset_block_start - 1] = GET_RANGE_SIZE((ccu_fix_block_end + 1), (vpu_reset_block_start - 1)); mvaInfoGraph[ccu_fix_block_end + 1] = priv_reserve; mvaInfoGraph[vpu_reset_block_start - 1] = priv_reserve; M4UINFO("[0x361, 0x500): start: 0x%x end: 0x%x mvaGraph: 0x%x\n", (ccu_fix_block_end + 1), (vpu_reset_block_start - 1), mvaGraph[ccu_fix_block_end + 1]); /*[0x500, 0x501)*/ /*set 1 to each bit14 of mvaGraph in vpu reserved region. *the operation can prevent allocating mva in vpu region *from m4u_do_mva_alloc. */ for (i = 0; i < VPU_RESET_VECTOR_BLOCK_NR; i++) MVA_SET_RESERVED(vpu_reset_block_start + i); mvaGraph[vpu_reset_block_start] = MVA_RESERVED_MASK | VPU_RESET_VECTOR_BLOCK_NR; mvaGraph[vpu_reset_block_end] = MVA_RESERVED_MASK | VPU_RESET_VECTOR_BLOCK_NR; mvaInfoGraph[vpu_reset_block_start] = priv_reserve; mvaInfoGraph[vpu_reset_block_end] = priv_reserve; M4UINFO("[0x500, 0x501): start: 0x%x end: 0x%x mvaGraph: 0x%x\n", vpu_reset_block_start, vpu_reset_block_end, mvaGraph[vpu_reset_block_start]); /*[0x501, 0x600)*/ mvaGraph[vpu_reset_block_end + 1] = GET_RANGE_SIZE((vpu_reset_block_end + 1), (vpu_fix_block_start - 1)); mvaGraph[vpu_fix_block_start - 1] = GET_RANGE_SIZE((vpu_reset_block_end + 1), vpu_fix_block_start - 1); mvaInfoGraph[vpu_reset_block_end + 1] = priv_reserve; mvaInfoGraph[vpu_fix_block_start - 1] = priv_reserve; M4UINFO("[0x501, 0x600): start: 0x%x end: 0x%x mvaGraph: 0x%x\n", (vpu_reset_block_end + 1), (vpu_fix_block_start - 1), mvaGraph[vpu_reset_block_end + 1]); /*[0x600, 0x7E0)*/ /*set 1 to each bit14 of mvaGraph in vpu reserved region*/ for (i = 0; i < VPU_FIX_BLOCK_NR; i++) MVA_SET_RESERVED(vpu_fix_block_start + i); mvaGraph[vpu_fix_block_start] = MVA_RESERVED_MASK | VPU_FIX_BLOCK_NR; mvaGraph[vpu_fix_block_end] = MVA_RESERVED_MASK | VPU_FIX_BLOCK_NR; mvaInfoGraph[vpu_fix_block_start] = priv_reserve; mvaInfoGraph[vpu_fix_block_end] = priv_reserve; M4UINFO("[0x600, 0x7E0): start: 0x%x end: 0x%x mvaGraph: 0x%x\n", vpu_fix_block_start, vpu_fix_block_end, mvaGraph[vpu_fix_block_start]); /*[0x7E0, 0xFFF]*/ mvaGraph[vpu_fix_block_end + 1] = GET_RANGE_SIZE((vpu_fix_block_end + 1), MVA_MAX_BLOCK_NR); mvaGraph[MVA_MAX_BLOCK_NR] = GET_RANGE_SIZE((vpu_fix_block_end + 1), MVA_MAX_BLOCK_NR); mvaInfoGraph[vpu_fix_block_end + 1] = priv_reserve; mvaInfoGraph[MVA_MAX_BLOCK_NR] = priv_reserve; M4UINFO("[0x7E0, 0xFFF]: start: 0x%x end: 0x%x mvaGraph: 0x%x\n", (vpu_fix_block_end + 1), MVA_MAX_BLOCK_NR, mvaGraph[vpu_fix_block_end + 1]); spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); } void m4u_mvaGraph_dump_raw(void) { int i; unsigned long irq_flags; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); M4ULOG_HIGH("[M4U_K] dump raw data of mvaGraph:============>\n"); for (i = 0; i < MVA_MAX_BLOCK_NR + 1; i++) M4ULOG_HIGH("0x%4x: 0x%08x\n", i, mvaGraph[i]); spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); } int is_in_vpu_region(unsigned int index, unsigned int nr) { unsigned int vpu_reset_block_start = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_START); unsigned int vpu_reset_block_end = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_END); unsigned int vpu_fix_block_start = MVAGRAPH_INDEX(VPU_FIX_MVA_START); unsigned int vpu_fix_block_end = MVAGRAPH_INDEX(VPU_FIX_MVA_END); int ret = 0; #if 0 M4UINFO("start = 0x%x, end = 0x%x nr = %x.\n", index, GET_END_INDEX(index, nr), nr); #endif if ((index >= vpu_reset_block_start && GET_END_INDEX(index, nr) <= vpu_reset_block_end) || (index >= vpu_fix_block_start && GET_END_INDEX(index, nr) <= vpu_fix_block_end)) ret = 1; #if 0 if (ret) M4UINFO("input region[0x%x - 0x%x] is in the vpu region.\n", index, GET_END_INDEX(index, nr)); #endif return ret; } int is_intersected_with_vpu_region(unsigned int start, unsigned int nr) { unsigned int vpu_reset_block_start = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_START); unsigned int vpu_reset_block_end = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_END); unsigned int vpu_fix_block_start = MVAGRAPH_INDEX(VPU_FIX_MVA_START); unsigned int vpu_fix_block_end = MVAGRAPH_INDEX(VPU_FIX_MVA_END); int ret = 0; /*4 cases: * |--------------|------|---------|------------|----------| * 0x0 0x500 0x501 0x600 0x800 0xFFF *case 1: start | end *case 2: start| end *case 3: start | end *case 4: start | end */ /*case 1: 1 <= start < 0x500 && 0x500<=end<=0xFFF*/ #if 0 M4UINFO("start = 0x%x, end = 0x%x nr = %x.\n", start, GET_END_INDEX(start, nr), nr); #endif if ((start >= 1 && start < vpu_reset_block_start) && (GET_END_INDEX(start, nr) >= vpu_reset_block_start && GET_END_INDEX(start, nr) <= MVA_MAX_BLOCK_NR)) ret = 1; /*case 2: 1 <= start < 0x501 && 0x501<=end<=0xFFF*/ if ((start >= 1 && start < (vpu_reset_block_end + 1)) && (GET_END_INDEX(start, nr) >= (vpu_reset_block_end + 1) && GET_END_INDEX(start, nr) <= MVA_MAX_BLOCK_NR)) ret = 1; /*case 3: 1 <= start < 0x600 && 0x600<=end<=0xFFF*/ if ((start >= 1 && start < vpu_fix_block_start) && (GET_END_INDEX(start, nr) >= vpu_fix_block_start && GET_END_INDEX(start, nr) <= MVA_MAX_BLOCK_NR)) ret = 1; /*case 4: 1 <=start < 0x800 && 0x800<=end<=0xFFF*/ if ((start >= 1 && start < (vpu_fix_block_end + 1)) && (GET_END_INDEX(start, nr) >= (vpu_fix_block_end + 1) && GET_END_INDEX(start, nr) <= MVA_MAX_BLOCK_NR)) ret = 1; #if 0 if (ret) M4UINFO("input region intersects to vpu region\n"); #endif return ret; } int is_in_ccu_region(unsigned int index, unsigned int nr) { unsigned int ccu_fix_block_start = MVAGRAPH_INDEX(CCU_FIX_MVA_START); unsigned int ccu_fix_block_end = MVAGRAPH_INDEX(CCU_FIX_MVA_END); if (index >= ccu_fix_block_start && GET_END_INDEX(index, nr) <= ccu_fix_block_end) return 1; return 0; } int is_intersected_with_ccu_region(unsigned int start, unsigned int nr) { unsigned int ccu_fix_block_start = MVAGRAPH_INDEX(CCU_FIX_MVA_START); unsigned int ccu_fix_block_end = MVAGRAPH_INDEX(CCU_FIX_MVA_END); int ret = 0; M4ULOG_LOW("%s:start = 0x%x, end = 0x%x nr = %x.\n", __func__, start, GET_END_INDEX(start, nr), nr); /*case 1: 1 <= start < 0x200 && 0x300<=end<=0xFFF*/ if ((start >= 1 && start < ccu_fix_block_start) && (GET_END_INDEX(start, nr) >= ccu_fix_block_start && GET_END_INDEX(start, nr) <= MVA_MAX_BLOCK_NR)) ret = 1; /*case 2: 1 <=start < 0x800 && 0x800<=end<=0xFFF*/ if ((start >= 1 && start < (ccu_fix_block_end + 1)) && (GET_END_INDEX(start, nr) >= (ccu_fix_block_end + 1) && GET_END_INDEX(start, nr) <= MVA_MAX_BLOCK_NR)) ret = 1; if (ret) M4ULOG_LOW("input region intersects to ccu region\n"); return ret; } /*1: y; 0: n*/ int check_reserved_region_integrity(unsigned int start, unsigned int nr) { int i, integrity = 0; for (i = 0; i < nr; i++) { if (!MVA_IS_RESERVED(start + i)) break; } if (i == nr) integrity = 1; else { M4UMSG("reserved blocks[0x%x-0x%x] corruptted at 0x%x\n", start, GET_END_INDEX(start, nr), i); } return integrity; } /*need to print integrity of vpu region*/ void m4u_mvaGraph_dump(void) { unsigned int start = 0, end = 0, size = 0; unsigned short index = 1, nr = 0; int i, max_bit, is_busy, is_reserve, integrity = 0; short frag[12] = { 0 }; unsigned short nr_free = 0, nr_alloc = 0; M4ULOG_HIGH( "[M4U_2.4] mva allocation info dump:====================>\n"); M4ULOG_HIGH( "start end size blocknum busy reserve integrity\n"); for (index = 1; index < MVA_MAX_BLOCK_NR + 1; index += nr) { start = index << MVA_BLOCK_SIZE_ORDER; nr = MVA_GET_NR(index); size = nr << MVA_BLOCK_SIZE_ORDER; end = start + size - 1; /* DO NOT call aee here directly to avoid recursive dump. */ if (nr == 0 || end <= start) { M4ULOG_HIGH("%s err: nr=%d, start=0x08x\n", __func__, nr, start); break; } if (MVA_IS_BUSY(index)) { is_busy = 1; if (MVA_IS_RESERVED(index)) is_reserve = 1; else is_reserve = 0; nr_alloc += nr; } else { /* mva region is free */ /* mva region is free */ is_busy = 0; if (MVA_IS_RESERVED(index)) is_reserve = 1; else is_reserve = 0; nr_free += nr; max_bit = 0; for (i = 0; i < 12; i++) { if (nr & (1 << i)) max_bit = i; } frag[max_bit]++; } /*verify if the reserve bit of each block in vpu region is set*/ if (is_in_ccu_region(index, nr)) { for (i = 0; i < nr; i++) { if (!MVA_IS_RESERVED(index + i)) break; } if (i == nr) integrity = 1; } else if (is_in_vpu_region(index, nr)) { for (i = 0; i < nr; i++) { if (!MVA_IS_RESERVED(index + i)) break; } if (i == nr) integrity = 1; } M4ULOG_HIGH( "0x%08x 0x%08x 0x%08x %4d %d %4d %8d\n", start, end, size, nr, is_busy, is_reserve, integrity); integrity = 0; } M4ULOG_HIGH("\n"); M4ULOG_HIGH( "[M4U_2.4] mva alloc summary: (unit: blocks)========================>\n"); M4ULOG_HIGH("free: %d , alloc: %d, total: %d\n", nr_free, nr_alloc, nr_free + nr_alloc); M4ULOG_HIGH( "[M4U_2.4] free region fragments in 2^x blocks unit:===============\n"); M4ULOG_HIGH( " 0 1 2 3 4 5 6 7 8 9 10 11\n"); M4ULOG_HIGH( "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n", frag[0], frag[1], frag[2], frag[3], frag[4], frag[5], frag[6], frag[7], frag[8], frag[9], frag[10], frag[11]); M4ULOG_HIGH( "[M4U_2.4] mva alloc dump done=========================<\n"); } void *mva_get_priv_ext(unsigned int mva) { void *priv = NULL; unsigned int index; unsigned long irq_flags; index = MVAGRAPH_INDEX(mva); if (index == 0 || index > MVA_MAX_BLOCK_NR) { M4UMSG("mvaGraph index is 0. mva=0x%x\n", mva); return NULL; } spin_lock_irqsave(&gMvaGraph_lock, irq_flags); /* find prev head/tail of this region */ while (mvaGraph[index] == 0) index--; if (MVA_IS_BUSY(index)) priv = mvaInfoGraph[index]; spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return priv; } int mva_foreach_priv(mva_buf_fn_t *fn, void *data) { unsigned short index = 1, nr = 0; unsigned int mva; void *priv; int ret; unsigned long irq_flags; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); for (index = 1; index < MVA_MAX_BLOCK_NR + 1; index += nr) { mva = index << MVA_BLOCK_SIZE_ORDER; nr = MVA_GET_NR(index); if (MVA_IS_BUSY(index)) { priv = mvaInfoGraph[index]; ret = fn(priv, mva, mva + nr * MVA_BLOCK_SIZE, data); if (ret) break; } } spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return 0; } unsigned int get_first_valid_mva(void) { unsigned short index = 1, nr = 0; unsigned int mva; void *priv; unsigned long irq_flags; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); for (index = 1; index < MVA_MAX_BLOCK_NR + 1; index += nr) { mva = index << MVA_BLOCK_SIZE_ORDER; nr = MVA_GET_NR(index); if (MVA_IS_BUSY(index)) { priv = mvaInfoGraph[index]; break; } } spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return mva; } void *mva_get_priv(unsigned int mva) { void *priv = NULL; unsigned int index; unsigned long irq_flags; index = MVAGRAPH_INDEX(mva); if (index == 0 || index > MVA_MAX_BLOCK_NR) { M4UMSG("mvaGraph index is 0. mva=0x%x\n", mva); return NULL; } spin_lock_irqsave(&gMvaGraph_lock, irq_flags); if (MVA_IS_BUSY(index)) priv = mvaInfoGraph[index]; spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return priv; } /*make sure @param priv is not NULL before call this function. *return 0 means non-vpu region access. *return -1 means input mva region *allocated by non-vpu port is in vpu reserved region. *or input mva region intersectst to vpu reserved region. *return 1 means vpu port alloc mva in vpu reserved region. */ int m4u_check_mva_region(unsigned int startIdx, unsigned int nr, void *priv) { struct m4u_buf_info_t *pMvaInfo = (struct m4u_buf_info_t *)priv; int is_in = 0, is_interseted = 0; int is_vpu_port = 0; #if defined(CONFIG_MACH_MT6775) || defined(CONFIG_MACH_MT6771) is_vpu_port = (pMvaInfo->port == M4U_PORT_VPU0) || (pMvaInfo->port == M4U_PORT_VPU1); #else is_vpu_port = (pMvaInfo->port == M4U_PORT_VPU); #endif /*check if input mva region is in vpu region. *if it's in vpu region, we check if it's non-vpu port */ #if 0 M4UINFO("%s: [0x%x - 0x%x]\n", __func__, startIdx, GET_END_INDEX(startIdx, nr)); #endif is_in = is_in_vpu_region(startIdx, nr); if (is_in && is_vpu_port) return 1; else if (is_in && !is_vpu_port) { M4UINFO( "[0x%x - 0x%x] requested by port(%d) is in vpu reserved region!\n", startIdx, GET_END_INDEX(startIdx, nr), pMvaInfo->port); return -1; } /*check if input mva region is intersected with vpu region*/ is_interseted = is_intersected_with_vpu_region(startIdx, nr); /*return 0 means other port normal alloction. *if it isn't in vpu region & insersected with vpu region. *it's non-vpu port alloc non-vpu reserved region. then return 0. */ if (!is_in && !is_interseted) return 0; M4UINFO( "[0x%x - 0x%x] requested by port(%d) intersects to vpu region!\n", startIdx, GET_END_INDEX(startIdx, nr), pMvaInfo->port); return -1; } static int __check_ccu_mva_region( unsigned int startIdx, unsigned int nr, void *priv) { struct m4u_buf_info_t *pMvaInfo = (struct m4u_buf_info_t *)priv; int is_in = 0, is_interseted = 0; int is_ccu_port = 0; #if defined(CONFIG_MACH_MT6771) is_ccu_port = (pMvaInfo->port == M4U_PORT_CCU0) || (pMvaInfo->port == M4U_PORT_CCU1) || (pMvaInfo->port == M4U_PORT_CAM_CCUI) || (pMvaInfo->port == M4U_PORT_CAM_CCUG) || (pMvaInfo->port == M4U_PORT_CAM_CCUO); #else is_ccu_port = (pMvaInfo->port == M4U_PORT_CAM_CCUI) || (pMvaInfo->port == M4U_PORT_CAM_CCUG) || (pMvaInfo->port == M4U_PORT_CAM_CCUO); #endif /*check if input mva region is in ccu region. *if it's in ccu region, we check if it's non-ccu port */ M4ULOG_LOW("%s: [0x%x - 0x%x]\n", __func__, startIdx, GET_END_INDEX(startIdx, nr)); is_in = is_in_ccu_region(startIdx, nr); if (is_in && is_ccu_port) return 1; else if (is_in && !is_ccu_port) { M4ULOG_MID( "[0x%x - 0x%x] requested by port(%d) is in ccu reserved region!\n", startIdx, GET_END_INDEX(startIdx, nr), pMvaInfo->port); return -1; } /*check if input mva region is intersected with vpu region*/ is_interseted = is_intersected_with_ccu_region(startIdx, nr); /*return 0 means other port normal alloction. *if it isn't in ccu region & insersected with ccu region. *it's non-ccu port alloc non-ccu reserved region. then return 0. */ if (!is_in && !is_interseted) return 0; M4ULOG_LOW( "[0x%x - 0x%x] requested by port(%d) intersects to ccu region!\n", startIdx, GET_END_INDEX(startIdx, nr), pMvaInfo->port); return -1; } /*m4u_do_mva_alloc should meet the following rules: *(1) vpu port should not m4u_do_mva_alloc vpu fix region, * but it can m4u_do_mva_alloc non-vpu fix region. *(2) non-vpu port can m4u_do_mva_alloc non-vpu fix region. */ unsigned int m4u_do_mva_alloc(unsigned long va, unsigned int size, void *priv) { unsigned short s, end; unsigned short new_start, new_end; unsigned short nr = 0; unsigned int mvaRegionStart; unsigned long startRequire, endRequire, sizeRequire; int region_status = 0; const short fix_index0 = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_START); const short fix_index1 = MVAGRAPH_INDEX(VPU_FIX_MVA_START); short gap_start_idx = GET_END_INDEX(fix_index0, VPU_RESET_VECTOR_BLOCK_NR) + 1; short gap_end_idx = fix_index1 - 1; short gap_nr = GET_RANGE_SIZE(gap_start_idx, gap_end_idx); const short ccu_fix_index_start = MVAGRAPH_INDEX(CCU_FIX_MVA_START); const short ccu_fix_index_end = MVAGRAPH_INDEX(CCU_FIX_MVA_END); const short ccu_gap_nr = GET_RANGE_SIZE(ccu_fix_index_end, fix_index0); unsigned long irq_flags; if (size == 0) return 0; /* ----------------------------------------------------- */ /* calculate mva block number */ startRequire = va & (~M4U_PAGE_MASK); endRequire = (va + size - 1) | M4U_PAGE_MASK; sizeRequire = endRequire - startRequire + 1; nr = (sizeRequire + MVA_BLOCK_ALIGN_MASK) >> MVA_BLOCK_SIZE_ORDER; if (fix_index1 == fix_index0) { gap_start_idx = MVAGRAPH_INDEX(VPU_FIX_MVA_START); gap_end_idx = GET_END_INDEX(fix_index0, VPU_RESET_VECTOR_BLOCK_NR) + 1; gap_nr = GET_RANGE_SIZE(gap_start_idx, gap_end_idx); } /* ----------------------------------------------- */ /* find the proper mva graph on 3 stages: * stage 1: find it in graph range [0x1-0x4FF ] * every mva graph is neighbouring. *cursor s navigate with plus the block nr in * each graph from start to end. *if there is one index whose graph's value is bigger * than the number we need, that means we found the requeired region. */ spin_lock_irqsave(&gMvaGraph_lock, irq_flags); /*find mva graph in range [0x1, 0x200]*/ for (s = 1; (s < ccu_fix_index_start) && (mvaGraph[s] < nr); s += (mvaGraph[s] & MVA_BLOCK_NR_MASK)) ; /*if we didn't find the proper graph on stage 1, *we will come to stage 2. * in the case, all graph in [0x1-0x1FF ] *is busy. jump ccu mva region. */ if (s == ccu_fix_index_start && nr <= ccu_gap_nr) { s = ccu_fix_index_end + 1; for (; (s < fix_index0) && (mvaGraph[s] < nr); s += (mvaGraph[s] & MVA_BLOCK_NR_MASK)) ; } if (s == fix_index0 && gap_nr >= 1) { /* stage 2: jump vpu reserved region *and find it in graph range [0x501-0x5FF ] * MUST check if block number of gap region is enough to alloc. * if not, we need to alloc from common region * only vinson have stage2,for vpu reserved region num is 2. * so for mt6775 and mt6771 we skip stage2. */ #if defined(CONFIG_MACH_MT6758) if (nr <= gap_nr) { M4ULOG_LOW( "stage 2: stopped cursor(%d) on stage 1\n", s); s = gap_start_idx; for (; (s <= gap_end_idx) && (mvaGraph[s] < nr); s += (mvaGraph[s] & MVA_BLOCK_NR_MASK)) ; } else goto stage3; #else goto stage3; #endif } /*if we didn't find the proper graph on stage 2, *we will come to stage 3. * in the case, the requeired nr may be *more than gap nr. * Or, all graph in grap region is busy. */ if (s == fix_index1) { /* stage 3: jump vpu reserved region and find it *in graph range [VPU_FIX_MVA_END + 1-0xFFF ] * allocate from common region directly. */ stage3: M4ULOG_LOW("stage 3: stopped cursor(%d) on stage 2\n", s); /*workaround for disp fb*/ #ifdef WORKAROUND_FOR_DISPLAY_FB s = MVAGRAPH_INDEX(VPU_FIX_MVA_END + 1) + (mvaGraph[MVAGRAPH_INDEX(VPU_FIX_MVA_END + 1)] & MVA_BLOCK_NR_MASK); #else s = MVAGRAPH_INDEX(VPU_FIX_MVA_END + 1); #endif for (; (s < (MVA_MAX_BLOCK_NR + 1)) && (mvaGraph[s] < nr); s += (mvaGraph[s] & MVA_BLOCK_NR_MASK)) ; } /*double check if mva region we got is in vpu reserved region. */ region_status = m4u_check_mva_region(s, nr, priv) || __check_ccu_mva_region(s, nr, priv); if (region_status) { spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); M4UMSG( "mva_alloc error: fault cursor(0x%x) access vpu region\n", s); return 0; } if (s > MVA_MAX_BLOCK_NR) { spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); M4UMSG( "mva_alloc error: no available MVA region for %d blocks!\n", nr); #ifdef M4U_PROFILE mmprofile_log_ex(M4U_MMP_Events[M4U_MMP_M4U_ERROR], MMPROFILE_FLAG_PULSE, size, s); #endif return 0; } /* ----------------------------------------------- */ /* alloc a mva region */ end = s + mvaGraph[s] - 1; if (unlikely(nr == mvaGraph[s])) { MVA_SET_BUSY(s); MVA_SET_BUSY(end); mvaInfoGraph[s] = priv; mvaInfoGraph[end] = priv; } else { new_end = s + nr - 1; new_start = new_end + 1; if (new_end > MVA_MAX_BLOCK_NR || new_start > MVA_MAX_BLOCK_NR) { M4UMSG( "mva_alloc error: mva region error! nr=%u, new_end=%u, s=%u, mvaGraph=0x%x\n", nr, new_end, s, mvaGraph[s]); return 0; } /* note: new_start may equals to end */ mvaGraph[new_start] = (mvaGraph[s] - nr); mvaGraph[new_end] = nr | MVA_BUSY_MASK; mvaGraph[s] = mvaGraph[new_end]; mvaGraph[end] = mvaGraph[new_start]; mvaInfoGraph[s] = priv; mvaInfoGraph[new_end] = priv; } spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); mvaRegionStart = (unsigned int)s; return (mvaRegionStart << MVA_BLOCK_SIZE_ORDER) + mva_pageOffset(va); } /*m4u_do_mva_alloc_fix is different with m4u_do_mva_alloc which find the free * and suitable region from the first one to the last. *Instead, m4u_do_mva_alloc_fix * is only used to find the fix one started from *formal parameter [mva] plus formal * parameter [va]. It's similar to mmap. *no matter how many [va] is, it will allocates * the whole fix region. it should notice the following: *(1) vpu fix region is protected by m4u_check_mva_region. *(2) if vpu port alloc vpu fix region by this, it must make sure reserved bit * mustn't be destroyed. *(3)There is no protection to non-vpu fix region now. if other fix regions are * taken by some ports, only m4u user can check this. *(4)because formal parameter [mva] == *fix mva region start, va + size - 1 should be *not more than fix mva region end. *@Param va[in] virtual address used by cpu, *we use it to get cpu page table offset. *@Param mva[in] modified virtual start address used *by m4u. user must provide the right one. *@Param size[in] the size of requeired sub range. *@Param priv[in] mva graph info. */ unsigned int m4u_do_mva_alloc_fix(unsigned long va, unsigned int mva, unsigned int size, void *priv) { unsigned short nr = 0; unsigned int startRequire, endRequire, sizeRequire; unsigned short startIdx = mva >> MVA_BLOCK_SIZE_ORDER; unsigned short endIdx; unsigned short region_start, region_end; int vpu_region_status = 0, is_in_vpu_region = 0; int ccu_region_status, is_in_ccu_region = 0; unsigned long irq_flags; if (size == 0) { M4UMSG("%s: size = %d\n", __func__, size); return 0; } mva = mva | (va & M4U_PAGE_MASK); /* ----------------------------------------------------- */ /* calculate mva block number */ startRequire = mva & (~MVA_BLOCK_ALIGN_MASK); endRequire = (mva + size - 1) | MVA_BLOCK_ALIGN_MASK; sizeRequire = endRequire - startRequire + 1; nr = (sizeRequire + MVA_BLOCK_ALIGN_MASK) >> MVA_BLOCK_SIZE_ORDER; ccu_region_status = __check_ccu_mva_region(startIdx, nr, priv); vpu_region_status = m4u_check_mva_region(startIdx, nr, priv); if (ccu_region_status == -1 && vpu_region_status == -1) return 0; else if (ccu_region_status == 1) is_in_ccu_region = 1; else if (vpu_region_status == 1) is_in_vpu_region = 1; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); region_start = startIdx; /* find prev head of this region. it may be the following relation: * |-----------|-------------|----------------|--------|----------| * 0x0 region_start startIdx endIdx region_end 0xFFF */ while (mvaGraph[region_start] == 0) region_start--; if (MVA_IS_BUSY(region_start) || (MVA_GET_NR(region_start) < nr + startIdx - region_start)) { M4UMSG("mva is inuse index=0x%x, mvaGraph=0x%x\n", region_start, mvaGraph[region_start]); mva = 0; goto out; } /* carveout startIdx~startIdx+nr-1 out of region_start */ endIdx = startIdx + nr - 1; region_end = region_start + MVA_GET_NR(region_start) - 1; if (startIdx == region_start && endIdx == region_end) { /* case 1: * |-----------|-----------------------------|-------------| * 0x0 region_start region_end 0xFFF * startIdx endIdx * alloc(start) alloc(end) */ MVA_SET_BUSY(startIdx); MVA_SET_BUSY(endIdx); if (is_in_ccu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } else if (is_in_vpu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } } else if (startIdx == region_start) { /* case 2: * |-----------|-------------------|----------|-------------| * 0x0 region_start endIdx region_end 0xFFF * startIdx * alloc(start) alloc(end) */ mvaGraph[startIdx] = nr | MVA_BUSY_MASK; mvaGraph[endIdx] = mvaGraph[startIdx]; mvaGraph[endIdx + 1] = region_end - endIdx; mvaGraph[region_end] = mvaGraph[endIdx + 1]; if (is_in_ccu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } else if (is_in_vpu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } } else if (endIdx == region_end) { /* case 3: * |-----------|------------|---------------|-------------| * 0x0 region_start startIdx region_end 0xFFF * endIdx * alloc(start) alloc(end) */ mvaGraph[region_start] = startIdx - region_start; mvaGraph[startIdx - 1] = mvaGraph[region_start]; mvaGraph[startIdx] = nr | MVA_BUSY_MASK; mvaGraph[endIdx] = mvaGraph[startIdx]; if (is_in_ccu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } else if (is_in_vpu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } } else { /* case 4: * |-----------|-----|-------|--------|------| * 0x0 region_start startIdx endIdx region_end 0xFFF * alloc(start) alloc(end) */ mvaGraph[region_start] = startIdx - region_start; mvaGraph[startIdx - 1] = mvaGraph[region_start]; mvaGraph[startIdx] = nr | MVA_BUSY_MASK; mvaGraph[endIdx] = mvaGraph[startIdx]; mvaGraph[endIdx + 1] = region_end - endIdx; mvaGraph[region_end] = mvaGraph[endIdx + 1]; if (is_in_ccu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } else if (is_in_vpu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } } mvaInfoGraph[startIdx] = priv; mvaInfoGraph[endIdx] = priv; out: spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return mva; } /*m4u_do_mva_alloc_start_from is used to allocate *a mva region start from iova_start to iova_end * for CCU & VPU. if [iova_start, iova_end] is busy, *it will return alloc failed. * for example: if [iova_start, iova_end] = *[0x10000000, 0xffffffff] size = 0x100000, * we will find a size = 0x100000 region started from *0x10000000 to 0xfffffff, until there is * a right one. * NOTICE: vpu should not use the api. *@Param va[in] virtual address used by cpu, *we use it to get cpu page table offset. *@Param mva[in] modified virtual start address used by *m4u. user must provide the right one. *@Param size[in] the size of requeired sub range. *@Param priv[in] mva graph info. */ unsigned int m4u_do_mva_alloc_start_from(unsigned long va, unsigned int mva, unsigned int size, void *priv) { unsigned short s = 0, end; unsigned short new_start, new_end; unsigned short nr = 0; unsigned int mvaRegionStart; unsigned long startRequire, endRequire, sizeRequire; unsigned short startIdx, endIdx; unsigned short region_start, region_end, next_region_start = 0; int vpu_region_status = 0, is_in_vpu_region = 0; int ccu_region_status, is_in_ccu_region = 0; unsigned long irq_flags; if (size == 0 || priv == NULL) { M4UMSG("%s: invalid size & port info\n", __func__); return 0; } /*TODO:Need to check if sub range in fix *range need to align to MVA_BLOCK_ALIGN_MASK */ /* find this region[startIdx ~ endIdx]. * |-----------|-|-----|----|--------| * 0x0 iova_start_idx iova_end_idx 0xFFF * startIdx endIdx */ startIdx = (mva + MVA_BLOCK_ALIGN_MASK) >> MVA_BLOCK_SIZE_ORDER; /* ----------------------------------------------------- */ /* calculate mva block number */ startRequire = va & (~M4U_PAGE_MASK); endRequire = (va + size - 1) | M4U_PAGE_MASK; sizeRequire = endRequire - startRequire + 1; nr = (sizeRequire + MVA_BLOCK_ALIGN_MASK) >> MVA_BLOCK_SIZE_ORDER; endIdx = startIdx + nr - 1; /*check [startIdx, endIdx] status*/ ccu_region_status = __check_ccu_mva_region(startIdx, nr, priv); vpu_region_status = m4u_check_mva_region(startIdx, nr, priv); if (ccu_region_status == -1 && vpu_region_status == -1) return 0; else if (ccu_region_status == 1) is_in_ccu_region = 1; else if (vpu_region_status == 1) is_in_vpu_region = 1; M4ULOG_LOW( "%s: iova_start_idx:0x%x, startIdx=0x%x, endIdx = 0x%x, nr= 0x%x\n", __func__, MVAGRAPH_INDEX(mva), startIdx, endIdx, nr); spin_lock_irqsave(&gMvaGraph_lock, irq_flags); /* use cursor region_start to find the region after the * region including the "startIdx"th block. * if we find the startIdx's neighbour and graph, we *maybe need to split it. * |-----|------|-|---|--------|------|----|-----| * 0x0 iova_start_idx iova_end_idx 0xFFF * startIdx endIdx(may be here) * region start next region start next region end */ for (region_start = 1; region_start < (MVA_MAX_BLOCK_NR + 1); region_start += MVA_GET_NR(region_start)) { /*error check*/ if ((mvaGraph[region_start] & MVA_BLOCK_NR_MASK) == 0) { m4u_mvaGraph_dump(); m4u_aee_print("%s: s=%d, 0x%x\n", __func__, s, mvaGraph[region_start]); } if ((region_start + MVA_GET_NR(region_start)) > startIdx) { next_region_start = region_start + MVA_GET_NR(region_start); break; } } if (region_start > MVA_MAX_BLOCK_NR) { M4UMSG( "%s:alloc mva fail,no available MVA for %d blocks\n", __func__, nr); spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return 0; } region_end = region_start + MVA_GET_NR(region_start) - 1; M4ULOG_LOW( "%s: found region_start(0x%x) region_end(0x%x) next_region_start(0x%x)\n", __func__, region_start, region_end, next_region_start); /*if not found, it means error.*/ if (next_region_start == 0) { M4UMSG("no enough mva to allocate.\n"); m4u_aee_print( "%s: region_start: %d, region_end= %d, region block count= %d\n", __func__, region_start, region_end, MVA_GET_NR(region_start)); } /*check the found region status.*/ if (MVA_IS_BUSY(region_start)) { /*if [region_start, region_end] is busy, need to *use another cursor s to continue. * |-----|------|-----|---|-----|--|---| * 0x0 iova_start_idx iova_end_idx 0xFFF * startIdx endIdx(may be here) * region start next region start next region end * s */ s = region_start; M4ULOG_LOW( "found region is busy. need to traverse again from s=0x%x.\n", s); } else { /*if [region_start, region_end] is free, need to check *whether the nr of [region_start, region_end] is *enough to alloc. *if enough, alloc it. if not, need to continue with "s = *next_region_start". * |-----|---|-----|---|------|----|-----| * 0x0 iova_start_idx iova_end_idx 0xFFF * startIdx endIdx(may be here) * region start next region start next region end * s */ if ((region_end - startIdx + 1) < nr) { s = next_region_start; M4UINFO( "the size of found region is not enough. need to traverse again from s=0x%x\n", s); } else M4UINFO( "found region is free. region_start=%d, s=%d\n", region_start, s); } /* now [region_start, region_end] is not free or enough to allocate. * so traverse mvaGraph to find the first right one. * here we should also need to traverse mvaGraph. */ if (s != 0) { /* find first match free region */ for (; s < (MVA_MAX_BLOCK_NR + 1); s += (mvaGraph[s] & MVA_BLOCK_NR_MASK)) { /*error check*/ if ((mvaGraph[s] & MVA_BLOCK_NR_MASK) == 0) { m4u_aee_print("%s: s=%d, 0x%x\n", __func__, s, mvaGraph[s]); m4u_mvaGraph_dump(); } if (MVA_GET_NR(s) > nr && !MVA_IS_BUSY(s)) { /*check [s, s + MVA_GET_NR(s) -1] status*/ ccu_region_status = __check_ccu_mva_region( s, MVA_GET_NR(s), priv); vpu_region_status = m4u_check_mva_region( s, MVA_GET_NR(s), priv); if (ccu_region_status == -1 && vpu_region_status == -1) continue; else if (ccu_region_status == 1) { is_in_ccu_region = 1; break; } else if (vpu_region_status == 1) { is_in_vpu_region = 1; break; } else if (ccu_region_status == 0 && vpu_region_status == 0) break; } } } if (s > MVA_MAX_BLOCK_NR) { spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); M4UMSG( "mva_alloc error: no available MVA region for %d blocks!\n", nr); #ifdef M4U_PROFILE mmprofile_log_ex(M4U_MMP_Events[M4U_MMP_M4U_ERROR], MMPROFILE_FLAG_PULSE, size, s); #endif return 0; } if (s != 0) M4ULOG_MID("after 2nd traverse, found region s = 0x%x\n", s); /* ----------------------------------------------- */ /*s==0 means startIdx == mva_start >> 20*/ if (s == 0) { /*[region_start, region_end] is free or enough to allocate. *it's same to m4u_do_mva_alloc_fix */ if (startIdx == region_start && endIdx == region_end) { MVA_SET_BUSY(startIdx); MVA_SET_BUSY(endIdx); if (is_in_ccu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } else if (is_in_vpu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } } else if (startIdx == region_start) { mvaGraph[startIdx] = nr | MVA_BUSY_MASK; mvaGraph[endIdx] = mvaGraph[startIdx]; mvaGraph[endIdx + 1] = region_end - endIdx; mvaGraph[region_end] = mvaGraph[endIdx + 1]; if (is_in_ccu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } else if (is_in_vpu_region) { MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } } else if (endIdx == region_end) { mvaGraph[region_start] = startIdx - region_start; mvaGraph[startIdx - 1] = mvaGraph[region_start]; mvaGraph[startIdx] = nr | MVA_BUSY_MASK; mvaGraph[endIdx] = mvaGraph[startIdx]; if (is_in_ccu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } else if (is_in_vpu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); } } else { mvaGraph[region_start] = startIdx - region_start; mvaGraph[startIdx - 1] = mvaGraph[region_start]; mvaGraph[startIdx] = nr | MVA_BUSY_MASK; mvaGraph[endIdx] = mvaGraph[startIdx]; mvaGraph[endIdx + 1] = region_end - endIdx; mvaGraph[region_end] = mvaGraph[endIdx + 1]; if (is_in_ccu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } else if (is_in_vpu_region) { MVA_SET_RESERVED(region_start); MVA_SET_RESERVED(startIdx - 1); MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); MVA_SET_RESERVED(region_end); } } mvaInfoGraph[startIdx] = priv; mvaInfoGraph[endIdx] = priv; s = startIdx; } else { /* alloc a mva region */ end = s + MVA_GET_NR(s) - 1; /*check [startIdx, endIdx] status*/ is_in_ccu_region = 0; is_in_vpu_region = 0; ccu_region_status = __check_ccu_mva_region(s, nr, priv); if (ccu_region_status == 1) is_in_ccu_region = 1; vpu_region_status = m4u_check_mva_region(s, MVA_GET_NR(s), priv); if (vpu_region_status == 1) is_in_vpu_region = 1; if (unlikely(nr == MVA_GET_NR(s))) { MVA_SET_BUSY(s); MVA_SET_BUSY(end); mvaInfoGraph[s] = priv; mvaInfoGraph[end] = priv; if (is_in_ccu_region) { MVA_SET_RESERVED(s); MVA_SET_RESERVED(end); } else if (is_in_vpu_region) { MVA_SET_RESERVED(s); MVA_SET_RESERVED(end); } } else { new_end = s + nr - 1; new_start = new_end + 1; /* note: new_start may equals to end */ mvaGraph[new_start] = (MVA_GET_NR(s) - nr); mvaGraph[new_end] = nr | MVA_BUSY_MASK; mvaGraph[s] = mvaGraph[new_end]; mvaGraph[end] = mvaGraph[new_start]; if (is_in_ccu_region) { MVA_SET_RESERVED(new_start); MVA_SET_RESERVED(new_end); MVA_SET_RESERVED(s); MVA_SET_RESERVED(end); } else if (is_in_vpu_region) { MVA_SET_RESERVED(new_start); MVA_SET_RESERVED(new_end); MVA_SET_RESERVED(s); MVA_SET_RESERVED(end); } mvaInfoGraph[s] = priv; mvaInfoGraph[new_end] = priv; } } spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); mvaRegionStart = (unsigned int)s; return (mvaRegionStart << MVA_BLOCK_SIZE_ORDER) + mva_pageOffset(va); } /*m4u_do_mva_free needs to care if non-vpu port wants to free vpu fix region.*/ #define RightWrong(x) ((x) ? "correct" : "error") int m4u_do_mva_free(unsigned int mva, unsigned int size) { unsigned short startIdx; unsigned short nr; unsigned short endIdx; unsigned int startRequire, endRequire, sizeRequire; unsigned short nrRequire, nr_tmp = 0; int vpu_region_status, is_in_vpu_region_flag = 0; int ccu_region_status, is_in_ccu_region_flag = 0; int ret = 0; struct m4u_buf_info_t *p_mva_info; int port; unsigned long irq_flags; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); startIdx = mva >> MVA_BLOCK_SIZE_ORDER; if (startIdx == 0 || startIdx > MVA_MAX_BLOCK_NR) { M4UMSG("mvaGraph index is 0. mva=0x%x\n", mva); spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return -1; } nr = mvaGraph[startIdx] & MVA_BLOCK_NR_MASK; endIdx = startIdx + nr - 1; p_mva_info = (struct m4u_buf_info_t *)mvaInfoGraph[startIdx]; if (size == 0 || nr == 0 || p_mva_info == NULL) { spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); M4UMSG("%s error: the input size = %d nr = %d mva_info = %p.\n", __func__, size, nr, p_mva_info); if (p_mva_info) M4UMSG("error port id = %d\n", p_mva_info->port); m4u_mvaGraph_dump(); return -1; } port = p_mva_info->port; /*check if reserved region meets free condition.*/ ccu_region_status = __check_ccu_mva_region( startIdx, nr, (void *)p_mva_info); vpu_region_status = m4u_check_mva_region( startIdx, nr, (void *)p_mva_info); if (ccu_region_status == -1 && vpu_region_status == -1) { spin_lock_irqsave(&gMvaGraph_lock, irq_flags); return -1; } else if (ccu_region_status == 1) is_in_ccu_region_flag = 1; else if (vpu_region_status == 1) is_in_vpu_region_flag = 1; /* -------------------------------- */ /* check the input arguments */ /* right condition: startIdx is not NULL && * region is busy && right module && right size */ startRequire = mva & (unsigned int)(~M4U_PAGE_MASK); endRequire = (mva + size - 1) | (unsigned int)M4U_PAGE_MASK; sizeRequire = endRequire - startRequire + 1; nrRequire = (sizeRequire + MVA_BLOCK_ALIGN_MASK) >> MVA_BLOCK_SIZE_ORDER; /* (sizeRequire>>MVA_BLOCK_SIZE_ORDER) + * ((sizeRequire&MVA_BLOCK_ALIGN_MASK)!=0); */ if (!(startIdx != 0 /* startIdx is not NULL */ && MVA_IS_BUSY(startIdx) && (nr == nrRequire))) { spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); M4UMSG("error to free mva========================>\n"); M4UMSG("BufSize=%d(unit:0x%xBytes) (expect %d) [%s]\n", nrRequire, MVA_BLOCK_SIZE, nr, RightWrong(nrRequire == nr)); M4UMSG("mva=0x%x, (IsBusy?)=%d (expect %d) [%s]\n", mva, MVA_IS_BUSY(startIdx), 1, RightWrong(MVA_IS_BUSY(startIdx))); m4u_mvaGraph_dump(); /* m4u_mvaGraph_dump_raw(); */ return -1; } mvaInfoGraph[startIdx] = NULL; mvaInfoGraph[endIdx] = NULL; /*now do the mva deallocation. if mva to be freed is in vpu fix region, *we should set reserved bit besides merging mva graph. if not in vpu *fix region, we should make sure reserved bit * is protected from destroying. */ if (is_in_ccu_region_flag || is_in_vpu_region_flag) { /* merge with followed region. * if endIdx + 1 is in vpu region, do the merge as before. * or, since we have set the reserved bit when alloc, * it's ok to do nothing. */ nr_tmp = MVA_GET_NR(endIdx + 1); if (is_in_ccu_region(endIdx + 1, nr_tmp) && !MVA_IS_BUSY(endIdx + 1)) { nr += nr_tmp; mvaGraph[endIdx] = 0; mvaGraph[endIdx + 1] = 0; MVA_SET_RESERVED(endIdx); MVA_SET_RESERVED(endIdx + 1); } /* merge with previous region * same operation as merging with followed region */ nr_tmp = MVA_GET_NR(startIdx - 1); if (is_in_ccu_region( GET_START_INDEX((startIdx - 1), nr_tmp), nr_tmp) && (!MVA_IS_BUSY(startIdx - 1))) { int pre_nr = nr_tmp; mvaGraph[startIdx] = 0; mvaGraph[startIdx - 1] = 0; MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(startIdx - 1); startIdx -= pre_nr; nr += pre_nr; } /* -------------------------------- */ /* set region flags */ mvaGraph[startIdx] = nr; mvaGraph[startIdx + nr - 1] = nr; MVA_SET_RESERVED(startIdx); MVA_SET_RESERVED(startIdx + nr - 1); } else { /* merge with followed region.*/ nr_tmp = MVA_GET_NR(endIdx + 1); if ((endIdx + 1 <= MVA_MAX_BLOCK_NR) && (!MVA_IS_BUSY(endIdx + 1))) { /* check if the followed region is in vpu region. * yes -> do nothing * no -> do the merging */ if (!MVA_IS_RESERVED(endIdx + 1)) { nr += nr_tmp; mvaGraph[endIdx] = 0; mvaGraph[endIdx + 1] = 0; } } /* merge with previous region * same operation as merging with followed region */ nr_tmp = MVA_GET_NR(startIdx - 1); if ((startIdx - 1) > 0 && (!MVA_IS_BUSY(startIdx - 1))) { if (!MVA_IS_RESERVED(startIdx - 1)) { int pre_nr = nr_tmp; mvaGraph[startIdx] = 0; mvaGraph[startIdx - 1] = 0; startIdx -= pre_nr; nr += pre_nr; } } /* -------------------------------- */ /* set region flags */ mvaGraph[startIdx] = nr; mvaGraph[startIdx + nr - 1] = nr; } spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); /*for debug*/ ret = check_reserved_region_integrity( MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_START), VPU_RESET_VECTOR_BLOCK_NR); if (!ret) M4UMSG( "VPU_RESET_VECTOR region is corruptted when port(%d) free mva(0x%x)\n", port, mva); ret = check_reserved_region_integrity(MVAGRAPH_INDEX(VPU_FIX_MVA_START), VPU_FIX_BLOCK_NR); if (!ret) M4UMSG( "VPU reserved data region is corruptted when port(%d) free mva(0x%x)\n", port, mva); return 0; } unsigned int get_last_free_graph_idx_in_stage1_region(void) { unsigned int index, nr; unsigned long irq_flags; index = MVAGRAPH_INDEX(VPU_RESET_VECTOR_FIX_MVA_START) - 1; spin_lock_irqsave(&gMvaGraph_lock, irq_flags); nr = MVA_GET_NR(index); index = GET_START_INDEX(index, nr); spin_unlock_irqrestore(&gMvaGraph_lock, irq_flags); return index; } unsigned int get_first_free_idx(void) { int first_valid_mva, first_free_idx; first_valid_mva = get_first_valid_mva(); first_free_idx = MVAGRAPH_INDEX(first_valid_mva) + MVA_GET_NR(first_valid_mva); return first_free_idx; }