Skip to content

Commit

Permalink
gpu: ion: Move MFC heap to different address
Browse files Browse the repository at this point in the history
Video hardware has the following requirements for
ION heaps:

1. MM heap must be at a higher address than FW heap.
2. MFC heap must at a higher address than FW
3. MM heap must be adjacent to FW heap.
   (There cannot be another heap between FW and MM heap)
4. MM and MFC heap cannot be more than 256MB away
   from the base address of the FW heap.

MM heap is configured as a reusable heap (FMEM heap) and FMEM
is carved out at a much higher address than the other heaps breaking
the above requirements. To support the above requirements the
MFC heap together with the FW heap must be carved out at the same
location as MM heap.

Signed-off-by: Olav Haugan <[email protected]>
(cherry picked from commit f6dc7749683486f797ab388394da370e0dd41e98)

Change-Id: Ibb53c0e17f1545e2ec5258c01e01101d68c209e1
Signed-off-by: Zhoulu Luo <[email protected]>

Conflicts:

	arch/arm/mach-msm/board-8960.c
	drivers/staging/qcache/fmem.c
	include/linux/fmem.h
  • Loading branch information
Olav Haugan authored and TomGiordano committed Apr 1, 2012
1 parent 041fceb commit e911890
Show file tree
Hide file tree
Showing 5 changed files with 387 additions and 6 deletions.
100 changes: 95 additions & 5 deletions arch/arm/mach-msm/board-8960.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,26 +310,34 @@ static int msm8960_paddr_to_memtype(unsigned int paddr)
return MEMTYPE_EBI1;
}

#define FMEM_ENABLED 1

#ifdef CONFIG_ION_MSM
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
static struct ion_cp_heap_pdata cp_mm_ion_pdata = {
.permission_type = IPT_TYPE_MM_CARVEOUT,
.align = PAGE_SIZE,
.reusable = FMEM_ENABLED,
.mem_is_fmem = FMEM_ENABLED,
};

static struct ion_cp_heap_pdata cp_mfc_ion_pdata = {
.permission_type = IPT_TYPE_MFC_SHAREDMEM,
.align = PAGE_SIZE,
.reusable = 0,
.mem_is_fmem = FMEM_ENABLED,
};

static struct ion_co_heap_pdata co_ion_pdata = {
.adjacent_mem_id = INVALID_HEAP_ID,
.align = PAGE_SIZE,
.mem_is_fmem = 0,
};

static struct ion_co_heap_pdata fw_co_ion_pdata = {
.adjacent_mem_id = ION_CP_MM_HEAP_ID,
.align = SZ_128K,
.mem_is_fmem = FMEM_ENABLED,
};
#endif
static struct ion_platform_data ion_pdata = {
Expand Down Expand Up @@ -419,11 +427,93 @@ static void reserve_ion_memory(void)
}
}
}
msm8960_reserve_table[MEMTYPE_EBI1].size += msm_ion_cp_mm_size;
msm8960_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MM_FW_SIZE;
msm8960_reserve_table[MEMTYPE_EBI1].size += MSM_ION_SF_SIZE;
msm8960_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MFC_SIZE;
msm8960_reserve_table[MEMTYPE_EBI1].size += MSM_ION_QSECOM_SIZE;
}

static void __init reserve_mem_for_ion(enum ion_memory_types mem_type,
unsigned long size)
{
msm8960_reserve_table[mem_type].size += size;
}

/**
* Reserve memory for ION and calculate amount of reusable memory for fmem.
* We only reserve memory for heaps that are not reusable. However, we only
* support one reusable heap at the moment so we ignore the reusable flag for
* other than the first heap with reusable flag set. Also handle special case
* for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be
* at a higher address than FW in addition to not more than 256MB away from the
* base address of the firmware. This means that if MM is reusable the other
* two heaps must be allocated in the same region as FW. This is handled by the
* mem_is_fmem flag in the platform data. In addition the MM heap must be
* adjacent to the FW heap for content protection purposes.
*/
static void __init reserve_ion_memory(void)
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
unsigned int reusable_count = 0;

adjust_mem_for_liquid();
fmem_pdata.size = 0;
fmem_pdata.reserved_size_low = 0;
fmem_pdata.reserved_size_high = 0;

/* We only support 1 reusable heap. Check if more than one heap
* is specified as reusable and set as non-reusable if found.
*/
for (i = 0; i < ion_pdata.nr; ++i) {
const struct ion_platform_heap *heap = &(ion_pdata.heaps[i]);

if (heap->type == ION_HEAP_TYPE_CP && heap->extra_data) {
struct ion_cp_heap_pdata *data = heap->extra_data;

reusable_count += (data->reusable) ? 1 : 0;

if (data->reusable && reusable_count > 1) {
pr_err("%s: Too many heaps specified as "
"reusable. Heap %s was not configured "
"as reusable.\n", __func__, heap->name);
data->reusable = 0;
}
}
}

for (i = 0; i < ion_pdata.nr; ++i) {
int reusable = 0;
int adjacent_heap_id = INVALID_HEAP_ID;
int mem_is_fmem = 0;
const struct ion_platform_heap *heap = &(ion_pdata.heaps[i]);

if (heap->extra_data) {
switch (heap->type) {
case ION_HEAP_TYPE_CP:
reusable = ((struct ion_cp_heap_pdata *)
heap->extra_data)->reusable;
mem_is_fmem = ((struct ion_cp_heap_pdata *)
heap->extra_data)->mem_is_fmem;
break;
case ION_HEAP_TYPE_CARVEOUT:
adjacent_heap_id = ((struct ion_co_heap_pdata *)
heap->extra_data)->adjacent_mem_id;
mem_is_fmem = ((struct ion_co_heap_pdata *)
heap->extra_data)->mem_is_fmem;
break;
default:
break;
}
}

if (mem_is_fmem && !reusable) {
if (adjacent_heap_id != INVALID_HEAP_ID)
fmem_pdata.reserved_size_low += heap->size;
else
fmem_pdata.reserved_size_high += heap->size;
}
if (mem_is_fmem)
fmem_pdata.size += heap->size;
else
reserve_mem_for_ion(MEMTYPE_EBI1, heap->size);
}
#endif
}

Expand Down
6 changes: 5 additions & 1 deletion drivers/gpu/ion/msm/msm_ion.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static void allocate_co_memory(struct ion_platform_heap *heap,
const struct fmem_data *fmem_info =
fmem_get_info();
heap->base = fmem_info->phys -
fmem_info->reserved_size;
fmem_info->reserved_size_low;
cp_data->virt_addr = fmem_info->virt;
pr_info("ION heap %s using FMEM\n",
shared_heap->name);
Expand Down Expand Up @@ -158,6 +158,10 @@ static void msm_ion_allocate(struct ion_platform_heap *heap)
heap->base = fmem_info->phys;
data->virt_addr = fmem_info->virt;
pr_info("ION heap %s using FMEM\n", heap->name);
} else if (data->mem_is_fmem) {
const struct fmem_data *fmem_info =
fmem_get_info();
heap->base = fmem_info->phys + fmem_info->size;
}
align = data->align;
break;
Expand Down
220 changes: 220 additions & 0 deletions drivers/staging/qcache/fmem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
*
* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/

#include <linux/fmem.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include "tmem.h"
#include <asm/mach/map.h>

struct fmem_data fmem_data;
enum fmem_state fmem_state;
static spinlock_t fmem_state_lock;

void *fmem_map_virtual_area(int cacheability)
{
unsigned long addr;
const struct mem_type *type;
int ret;

addr = (unsigned long) fmem_data.area->addr;
type = get_mem_type(cacheability);
ret = ioremap_page_range(addr, addr + fmem_data.size,
fmem_data.phys, __pgprot(type->prot_pte));
if (ret)
return ERR_PTR(ret);

fmem_data.virt = fmem_data.area->addr;

return fmem_data.virt;
}

void fmem_unmap_virtual_area(void)
{
unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size);
fmem_data.virt = NULL;
}

static int fmem_probe(struct platform_device *pdev)
{
struct fmem_platform_data *pdata = pdev->dev.platform_data;

fmem_data.phys = pdata->phys + pdata->reserved_size_low;
fmem_data.size = pdata->size - pdata->reserved_size_low -
pdata->reserved_size_high;
fmem_data.reserved_size_low = pdata->reserved_size_low;
fmem_data.reserved_size_high = pdata->reserved_size_high;

if (!fmem_data.size)
return -ENODEV;

fmem_data.area = get_vm_area(fmem_data.size, VM_IOREMAP);
if (!fmem_data.area)
return -ENOMEM;

if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) {
remove_vm_area(fmem_data.area->addr);
return -ENOMEM;
}
pr_info("fmem phys %lx virt %p size %lx\n",
fmem_data.phys, fmem_data.virt, fmem_data.size);

spin_lock_init(&fmem_state_lock);

return 0;
}

static int fmem_remove(struct platform_device *pdev)
{
return 0;
}

static struct platform_driver fmem_driver = {
.probe = fmem_probe,
.remove = fmem_remove,
.driver = { .name = "fmem" }
};

#ifdef CONFIG_SYSFS
static ssize_t fmem_state_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
if (fmem_state == FMEM_T_STATE)
return snprintf(buf, 3, "t\n");
else if (fmem_state == FMEM_C_STATE)
return snprintf(buf, 3, "c\n");
else if (fmem_state == FMEM_UNINITIALIZED)
return snprintf(buf, 15, "uninitialized\n");
return snprintf(buf, 3, "?\n");
}

static ssize_t fmem_state_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret = -EINVAL;

if (!strncmp(buf, "t", 1))
ret = fmem_set_state(FMEM_T_STATE);
else if (!strncmp(buf, "c", 1))
ret = fmem_set_state(FMEM_C_STATE);
if (ret)
return ret;
return 1;
}

static struct kobj_attribute fmem_state_attr = {
.attr = { .name = "state", .mode = 0644 },
.show = fmem_state_show,
.store = fmem_state_store,
};

static struct attribute *fmem_attrs[] = {
&fmem_state_attr.attr,
NULL,
};

static struct attribute_group fmem_attr_group = {
.attrs = fmem_attrs,
.name = "fmem",
};

static int fmem_create_sysfs(void)
{
int ret = 0;

ret = sysfs_create_group(mm_kobj, &fmem_attr_group);
if (ret)
pr_err("fmem: can't create sysfs\n");
return ret;
}

#endif

static int __init fmem_init(void)
{
return platform_driver_register(&fmem_driver);
}

static void __exit fmem_exit(void)
{
platform_driver_unregister(&fmem_driver);
}

struct fmem_data *fmem_get_info(void)
{
return &fmem_data;
}
EXPORT_SYMBOL(fmem_get_info);

void lock_fmem_state(void)
{
spin_lock(&fmem_state_lock);
}

void unlock_fmem_state(void)
{
spin_unlock(&fmem_state_lock);
}

int fmem_set_state(enum fmem_state new_state)
{
int ret = 0;
int create_sysfs = 0;

lock_fmem_state();
if (fmem_state == new_state)
goto out;

if (fmem_state == FMEM_UNINITIALIZED) {
if (new_state == FMEM_T_STATE) {
tmem_enable();
create_sysfs = 1;
goto out_set;
}
if (new_state == FMEM_C_STATE) {
ret = -EINVAL;
goto out;
}
}

if (new_state == FMEM_T_STATE) {
void *v;
v = fmem_map_virtual_area(MT_DEVICE_CACHED);
if (IS_ERR_OR_NULL(v)) {
ret = PTR_ERR(v);
goto out;
}
tmem_enable();
} else {
tmem_disable();
fmem_unmap_virtual_area();
}

out_set:
fmem_state = new_state;
out:
unlock_fmem_state();
#ifdef CONFIG_SYSFS
if (create_sysfs)
fmem_create_sysfs();
#endif
return ret;
}
EXPORT_SYMBOL(fmem_set_state);

arch_initcall(fmem_init);
module_exit(fmem_exit);
Loading

0 comments on commit e911890

Please sign in to comment.