FMEM: allocate non-fixed FMEM from memory pool

On platforms whose FMEM is not at a fixed place
and which don't have DMM (such as 7x27a), FMEM
and the EBI memory pool would incorrectly overlap,
and the same memory would be used both as FMEM
and as memory allocated by allocate_contiguous_memory()
at the same time leading to crashes and strange behavior.

There are some unsolved problems related to
allocating FMEM from unstable memory, so we have
decided not to do this any longer. This decision allows
us to simply allocate non-fixed FMEM from the
EBI memory pool (this will prevent FMEM's memory
from being used by another allocator at the same time).
In this case FMEM will appear to "overlap" with
the memory pool, but this now OK as FMEM is allocated
from it.

This also re-enables FMEM on 7x27a and 8625, which
had been disabled due to the above-mentioned bug.

CRs-Fixed: 354254
Change-Id: I8e0bba332c7ddcac1d280de9642078fdfe61bbcc
Signed-off-by: Larry Bassel <lbassel@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index a3afdb0..c26b8b9 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -806,15 +806,15 @@
 	msm_reserve();
 	if (msm8960_fmem_pdata.size) {
 #if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
-		msm8960_fmem_pdata.phys = reserve_info->fixed_area_start +
-			MSM_MM_FW_SIZE;
-		pr_info("mm fw at %lx (fixed) size %x\n",
-			reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
-		pr_info("fmem start %lx (fixed) size %lx\n",
-			msm8960_fmem_pdata.phys, msm8960_fmem_pdata.size);
-#else
-		msm8960_fmem_pdata.phys =
-			reserve_memory_for_fmem(msm8960_fmem_pdata.size);
+		if (reserve_info->fixed_area_size) {
+			msm8960_fmem_pdata.phys =
+				reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
+			pr_info("mm fw at %lx (fixed) size %x\n",
+				reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
+			pr_info("fmem start %lx (fixed) size %lx\n",
+				msm8960_fmem_pdata.phys,
+				msm8960_fmem_pdata.size);
+		}
 #endif
 	}
 }
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 2cf3b8d..df4ca83 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -48,6 +48,7 @@
 #include <mach/msm_battery.h>
 #include <linux/smsc911x.h>
 #include <linux/atmel_maxtouch.h>
+#include <linux/fmem.h>
 #include <linux/msm_adc.h>
 #include "devices.h"
 #include "timer.h"
@@ -435,6 +436,9 @@
 	.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
 	.cached = 1,
 	.memory_type = MEMTYPE_EBI1,
+	.request_region = request_fmem_c_region,
+	.release_region = release_fmem_c_region,
+	.reusable = 1,
 };
 
 static struct platform_device android_pmem_adsp_device = {
@@ -756,6 +760,14 @@
 static void msm7x27a_cfg_uart2dm_serial(void) { }
 #endif
 
+struct fmem_platform_data fmem_pdata;
+
+struct platform_device fmem_device = {
+	.name = "fmem",
+	.id = 1,
+	.dev = { .platform_data = &fmem_pdata },
+};
+
 static struct platform_device *rumi_sim_devices[] __initdata = {
 	&msm_device_dmov,
 	&msm_device_smd,
@@ -793,6 +805,7 @@
 	&android_pmem_device,
 	&android_pmem_adsp_device,
 	&android_pmem_audio_device,
+	&fmem_device,
 	&msm_device_nand,
 	&msm_device_snd,
 	&msm_device_adspdec,
@@ -843,8 +856,19 @@
 	},
 };
 
+#ifdef CONFIG_ANDROID_PMEM
+static struct android_pmem_platform_data *pmem_pdata_array[] __initdata = {
+		&android_pmem_adsp_pdata,
+		&android_pmem_audio_pdata,
+		&android_pmem_pdata,
+};
+#endif
+
 static void __init size_pmem_devices(void)
 {
+#ifdef CONFIG_ANDROID_PMEM
+	unsigned int i;
+	unsigned int reusable_count = 0;
 
 	if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) {
 		pmem_mdp_size = MSM7x25A_MSM_PMEM_MDP_SIZE;
@@ -854,11 +878,30 @@
 		pmem_adsp_size = MSM_PMEM_ADSP_SIZE;
 	}
 
-#ifdef CONFIG_ANDROID_PMEM
 	android_pmem_adsp_pdata.size = pmem_adsp_size;
 	android_pmem_pdata.size = pmem_mdp_size;
 	android_pmem_audio_pdata.size = pmem_audio_size;
+
+	fmem_pdata.size = 0;
+
+	/* Find pmem devices that should use FMEM (reusable) memory.
+	 */
+	for (i = 0; i < ARRAY_SIZE(pmem_pdata_array); ++i) {
+		struct android_pmem_platform_data *pdata = pmem_pdata_array[i];
+
+		if (!reusable_count && pdata->reusable)
+			fmem_pdata.size += pdata->size;
+
+		reusable_count += (pdata->reusable) ? 1 : 0;
+
+		if (pdata->reusable && reusable_count > 1) {
+			pr_err("%s: Too many PMEM devices specified as reusable. PMEM device %s was not configured as reusable.\n",
+				__func__, pdata->name);
+			pdata->reusable = 0;
+		}
+	}
 #endif
+
 }
 
 static void __init reserve_memory_for(struct android_pmem_platform_data *p)
@@ -869,9 +912,10 @@
 static void __init reserve_pmem_memory(void)
 {
 #ifdef CONFIG_ANDROID_PMEM
-	reserve_memory_for(&android_pmem_adsp_pdata);
-	reserve_memory_for(&android_pmem_pdata);
-	reserve_memory_for(&android_pmem_audio_pdata);
+	unsigned int i;
+	for (i = 0; i < ARRAY_SIZE(pmem_pdata_array); ++i)
+		reserve_memory_for(pmem_pdata_array[i]);
+
 	msm7x27a_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size;
 #endif
 }
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 0db160e..ccb18b3 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -332,29 +332,6 @@
 	}
 }
 
-unsigned long __init reserve_memory_for_fmem(unsigned long fmem_size)
-{
-	struct membank *mb;
-	int ret;
-	unsigned long fmem_phys;
-
-	if (!fmem_size)
-		return 0;
-
-	mb = &meminfo.bank[meminfo.nr_banks - 1];
-	/*
-	 * Placing fmem at the top of memory causes multimedia issues.
-	 * Instead, place it 1 page below the top of memory to prevent
-	 * the issues from occurring.
-	 */
-	fmem_phys = mb->start + (mb->size - fmem_size) - PAGE_SIZE;
-	ret = memblock_remove(fmem_phys, fmem_size);
-	BUG_ON(ret);
-
-	pr_info("fmem start %lx size %lx\n", fmem_phys, fmem_size);
-	return fmem_phys;
-}
-
 static void __init initialize_mempools(void)
 {
 	struct mem_pool *mpool;
diff --git a/drivers/staging/qcache/fmem.c b/drivers/staging/qcache/fmem.c
index 4250ff5..7a95880 100644
--- a/drivers/staging/qcache/fmem.c
+++ b/drivers/staging/qcache/fmem.c
@@ -60,6 +60,10 @@
 {
 	struct fmem_platform_data *pdata = pdev->dev.platform_data;
 
+	if (!pdata->phys)
+		pdata->phys = allocate_contiguous_ebi_nomap(pdata->size,
+			PAGE_SIZE);
+
 #ifdef CONFIG_MEMORY_HOTPLUG
 	fmem_section_start = pdata->phys >> PA_SECTION_SHIFT;
 	fmem_section_end = (pdata->phys - 1 + pdata->size) >> PA_SECTION_SHIFT;