blob: 290ba332b86d68e174a05e8e08db175980987f6b [file] [log] [blame]
/* Copyright (c) 2010-2011, 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 <mach/msm_subsystem_map.h>
#include <linux/memory_alloc.h>
#include <linux/iommu.h>
#include <asm/sizes.h>
#include <asm/page.h>
#include <linux/init.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
struct msm_iommu_domain {
int domain_idx;
int iova_pool_idx;
};
enum {
GLOBAL_DOMAIN,
VIDEO_DOMAIN,
EMPTY_DOMAIN,
MAX_DOMAINS
};
enum {
GLOBAL_MEMORY_POOL,
VIDEO_FIRMWARE_POOL,
VIDEO_ALLOC_POOL,
};
struct {
char *name;
int domain;
} msm_iommu_ctx_names[] = {
/* Camera */
{
.name = "vpe_src",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "vpe_dst",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "vfe_imgwr",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "vfe_misc",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "ijpeg_src",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "ijpeg_dst",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "jpegd_src",
.domain = GLOBAL_DOMAIN,
},
/* Camera */
{
.name = "jpegd_dst",
.domain = GLOBAL_DOMAIN,
},
/* Display */
{
.name = "mdp_vg1",
.domain = GLOBAL_DOMAIN,
},
/* Display */
{
.name = "mdp_vg2",
.domain = GLOBAL_DOMAIN,
},
/* Display */
{
.name = "mdp_rgb1",
.domain = GLOBAL_DOMAIN,
},
/* Display */
{
.name = "mdp_rgb2",
.domain = GLOBAL_DOMAIN,
},
/* Rotator */
{
.name = "rot_src",
.domain = GLOBAL_DOMAIN,
},
/* Rotator */
{
.name = "rot_dst",
.domain = GLOBAL_DOMAIN,
},
/* Video */
{
.name = "vcodec_a_mm1",
.domain = VIDEO_DOMAIN,
},
/* Video */
{
.name = "vcodec_b_mm2",
.domain = VIDEO_DOMAIN,
},
/* Video */
{
.name = "vcodec_a_stream",
.domain = VIDEO_DOMAIN,
},
};
static struct iommu_domain *msm_iommu_domains[MAX_DOMAINS];
static struct mem_pool msm_iommu_iova_pools[] = {
[GLOBAL_MEMORY_POOL] = {
.paddr = SZ_4K,
.size = SZ_2G - SZ_4K,
},
/*
* The video hardware has several constraints:
* 1) The start address for firmware must be 128K aligned
* 2) The video firmware must exist at a lower address than
* all other video allocations
* 3) Video allocations cannot be more than 256MB away from the
* firmware
*
* Splitting the video pools makes sure that firmware will
* always be lower than regular allocations and the maximum
* size of 256MB will be enforced.
*/
[VIDEO_FIRMWARE_POOL] = {
.paddr = SZ_128K,
.size = SZ_16M - SZ_128K,
},
[VIDEO_ALLOC_POOL] = {
.paddr = SZ_16M,
.size = SZ_256M - SZ_16M - SZ_128K,
}
};
static struct msm_iommu_domain msm_iommu_subsystems[] = {
[MSM_SUBSYSTEM_VIDEO] = {
.domain_idx = VIDEO_DOMAIN,
.iova_pool_idx = VIDEO_ALLOC_POOL,
},
[MSM_SUBSYSTEM_VIDEO_FWARE] = {
.domain_idx = VIDEO_DOMAIN,
.iova_pool_idx = VIDEO_FIRMWARE_POOL,
},
[MSM_SUBSYSTEM_CAMERA] = {
.domain_idx = GLOBAL_DOMAIN,
.iova_pool_idx = GLOBAL_MEMORY_POOL,
},
[MSM_SUBSYSTEM_DISPLAY] = {
.domain_idx = GLOBAL_DOMAIN,
.iova_pool_idx = GLOBAL_MEMORY_POOL,
},
[MSM_SUBSYSTEM_ROTATOR] = {
.domain_idx = GLOBAL_DOMAIN,
.iova_pool_idx = GLOBAL_MEMORY_POOL,
},
};
struct iommu_domain *msm_subsystem_get_domain(int subsys_id)
{
int id = msm_iommu_subsystems[subsys_id].domain_idx;
return msm_iommu_domains[id];
}
struct mem_pool *msm_subsystem_get_pool(int subsys_id)
{
int id = msm_iommu_subsystems[subsys_id].iova_pool_idx;
return &msm_iommu_iova_pools[id];
}
static int __init msm_subsystem_iommu_init(void)
{
int i;
for (i = 0; i < (ARRAY_SIZE(msm_iommu_domains) - 1); i++)
msm_iommu_domains[i] = iommu_domain_alloc(0);
for (i = 0; i < ARRAY_SIZE(msm_iommu_iova_pools); i++) {
mutex_init(&msm_iommu_iova_pools[i].pool_mutex);
msm_iommu_iova_pools[i].gpool = gen_pool_create(PAGE_SHIFT, -1);
if (!msm_iommu_iova_pools[i].gpool) {
pr_err("%s: could not allocate iova pool. iommu"
" programming will not work with iova space"
" %d\n", __func__, i);
continue;
}
if (gen_pool_add(msm_iommu_iova_pools[i].gpool,
msm_iommu_iova_pools[i].paddr,
msm_iommu_iova_pools[i].size,
-1)) {
pr_err("%s: could not add memory to iova pool. iommu"
" programming will not work with iova space"
" %d\n", __func__, i);
gen_pool_destroy(msm_iommu_iova_pools[i].gpool);
msm_iommu_iova_pools[i].gpool = NULL;
continue;
}
}
for (i = 0; i < ARRAY_SIZE(msm_iommu_ctx_names); i++) {
int domain_idx;
struct device *ctx = msm_iommu_get_ctx(
msm_iommu_ctx_names[i].name);
if (!ctx)
continue;
domain_idx = msm_iommu_ctx_names[i].domain;
if (!msm_iommu_domains[domain_idx])
continue;
if (iommu_attach_device(msm_iommu_domains[domain_idx], ctx)) {
pr_err("%s: could not attach domain %d to context %s."
" iommu programming will not occur.\n",
__func__, domain_idx,
msm_iommu_ctx_names[i].name);
msm_iommu_subsystems[i].domain_idx = EMPTY_DOMAIN;
continue;
}
}
return 0;
}
device_initcall(msm_subsystem_iommu_init);