blob: 87df5db3fe4f93d014aed38adde002a95f19b9ab [file] [log] [blame]
/*
* linux/drivers/video/ti81xx/vpss/sbuf.c
*
* VPSS shared buffer(A8 and M3) driver for TI 81XX
*
* Copyright (C) 2009 TI
* Author: Yihe Hu <yihehu@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define VPSS_SUBMODULE_NAME "SHRBUF"
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <asm/setup.h>
#include "core.h"
/*shared buffer structure*/
struct sbuf_info {
struct list_head alloc_list;
u32 paddr;
u32 pages;
void *vaddr;
void __iomem *base;
};
/*allocate buffer structure*/
struct sbuf_alloc {
struct list_head list;
u32 paddr;
u32 pages;
};
static struct sbuf_info *sbinfo;
static DEFINE_MUTEX(sbuf_mutex);
/******************NOTE***************************
The following are the sharing buffer address based
on different platform. All these buffer should be
restrictly non-cacheable from M3 side and can not be
accessable by any other SW component other than
this driver.
************************************************/
/*TI816X*/
#define TI816X_SHARING_BUFFER_BASE 0xB2C00000
/*C6A18X*/
#define TIC6A81XX_SHARING_BUFFER_BASE 0x8DE00000
/*TI814X*/
#define TI814X_SHARING_BUFFER_BASE 0xCFE00000
#define TI81XX_SHARING_BUFFER_SIZE (2*1024*1024)
/*create list node*/
static struct sbuf_alloc *sbuf_create_allocation(
u32 start,
u32 pages)
{
struct sbuf_alloc *sba;
struct sbuf_alloc *new;
new = kzalloc(sizeof(*sba), GFP_KERNEL);
if (!new)
return NULL;
new->paddr = start;
new->pages = pages;
list_for_each_entry(sba, &sbinfo->alloc_list, list) {
if (sba->paddr > new->paddr)
break;
}
list_add_tail(&new->list, &sba->list);
return new;
}
/*free the allocation*/
static void sbuf_free_allocation(struct sbuf_alloc *sba)
{
list_del(&sba->list);
kfree(sba);
}
/*internal allocate buffer*/
static void *_vps_sbuf_alloc(u32 pages, u32 *paddr)
{
struct sbuf_alloc *sballoc;
u32 start, end;
void *vaddr;
start = sbinfo->paddr;
list_for_each_entry(sballoc, &sbinfo->alloc_list, list) {
end = sballoc->paddr;
if (end - start >= pages << PAGE_SHIFT)
goto found;
start = sballoc->paddr + (sballoc->pages << PAGE_SHIFT);
}
end = sbinfo->paddr + (sbinfo->pages << PAGE_SHIFT);
found:
if (end - start < pages << PAGE_SHIFT)
return NULL;
if (sbuf_create_allocation(start, pages) == NULL)
return NULL;
*paddr = start;
vaddr = sbinfo->vaddr + (start - sbinfo->paddr);
VPSSDBG("FOUND 0x%x, end 0x%x, map vir 0x%p\n", start, end, vaddr);
return vaddr;
}
/*allocate buffer*/
void *vps_sbuf_alloc(size_t size, u32 *paddr)
{
void *vaddr;
unsigned pages;
pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
mutex_lock(&sbuf_mutex);
vaddr = _vps_sbuf_alloc(pages, paddr);
mutex_unlock(&sbuf_mutex);
return vaddr;
}
/*free buffer*/
int vps_sbuf_free(u32 paddr, void *vaddr, size_t size)
{
struct sbuf_alloc *sba;
u32 start, end;
size = PAGE_ALIGN(size);
mutex_lock(&sbuf_mutex);
list_for_each_entry(sba, &sbinfo->alloc_list, list) {
start = sba->paddr;
end = sba->paddr + (sba->pages << PAGE_SHIFT);
if (start >= paddr && (end <= paddr + size)) {
sbuf_free_allocation(sba);
VPSSDBG("free mem paddr 0x%x vaddr 0x%p size %d\n",
paddr, vaddr, size);
break;
}
}
mutex_unlock(&sbuf_mutex);
return 0;
}
int __init vps_sbuf_init(const char *sbaddr, const char *sbsize)
{
int r = 0;
uint size = 0;
uint addr = 0;
char *str = (char *)sbsize;
VPSSDBG("sbuf init\n");
/*parse commond arguments first*/
if (sbaddr)
addr = PAGE_ALIGN(simple_strtol(sbaddr, NULL, 16));
/*use the default value instead if not set in the command*/
if (!addr) {
if (cpu_is_ti816x())
addr = TIC6A81XX_SHARING_BUFFER_BASE;
else
addr = TIC6A81XX_SHARING_BUFFER_BASE;
}
/*parse the commond argumetn for the payload size*/
if (sbsize)
size = memparse(str, &str);
/*use the value instead if not set in the command*/
if ((!size) || (size > TI81XX_SHARING_BUFFER_SIZE))
size = TI81XX_SHARING_BUFFER_SIZE;
/*alloce structrue*/
sbinfo = kzalloc(sizeof(struct sbuf_info), GFP_KERNEL);
if (sbinfo == NULL) {
VPSSERR("failed to allocate\n");
r = -ENOMEM;
goto exit;
}
/*IO remap to non-cacheable space*/
sbinfo->base = ioremap_nocache(addr,
size);
if (sbinfo->base == NULL) {
VPSSERR("failed to remap.\n");
r = -ENODEV;
goto exit;
}
/*store the information*/
sbinfo->vaddr = (void *)sbinfo->base;
sbinfo->paddr = addr;
sbinfo->pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
VPSSDBG("map 0x%x to 0x%p with size %d\n",
sbinfo->paddr,
sbinfo->base,
sbinfo->pages << PAGE_SHIFT);
INIT_LIST_HEAD(&sbinfo->alloc_list);
return 0;
exit:
vps_sbuf_deinit();
return r;
}
int __exit vps_sbuf_deinit(void)
{
struct sbuf_alloc *sba, *next;
VPSSDBG("sbuf deinit\n");
/*free any remaining buffer*/
if (!list_empty(&sbinfo->alloc_list)) {
list_for_each_entry_safe(sba,
next,
&sbinfo->alloc_list,
list)
sbuf_free_allocation(sba);
}
if (sbinfo->base)
iounmap(sbinfo->base);
kfree(sbinfo);
return 0;
}