mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
add vpu_mem module by chm
增加 VPU 内存管理模块,目前还是最简单的内存管理策略,没有做优化
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
obj-y += timer.o io.o devices.o iomux.o clock.o rk29-pl330.o dma.o gpio.o
|
||||
obj-$(CONFIG_RK29_VPU) += vpu.o
|
||||
obj-$(CONFIG_RK29_VPU) += vpu.o vpu_mem.o
|
||||
obj-$(CONFIG_MACH_RK29SDK) += board-rk29sdk.o board-rk29sdk-key.o
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <mach/rk29_nand.h>
|
||||
#include <mach/rk29_camera.h> /* ddl@rock-chips.com : camera support */
|
||||
#include <media/soc_camera.h> /* ddl@rock-chips.com : camera support */
|
||||
#include <mach/vpu_mem.h>
|
||||
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
@@ -81,27 +83,27 @@ static struct rk29_gpio_bank rk29_gpiobankinit[] = {
|
||||
{
|
||||
.id = RK29_ID_GPIO1,
|
||||
.offset = RK29_GPIO1_BASE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = RK29_ID_GPIO2,
|
||||
.offset = RK29_GPIO2_BASE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = RK29_ID_GPIO3,
|
||||
.offset = RK29_GPIO3_BASE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = RK29_ID_GPIO4,
|
||||
.offset = RK29_GPIO4_BASE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = RK29_ID_GPIO5,
|
||||
.offset = RK29_GPIO5_BASE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = RK29_ID_GPIO6,
|
||||
.offset = RK29_GPIO6_BASE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/*****************************************************************************************
|
||||
@@ -320,19 +322,18 @@ static struct platform_device android_pmem_device = {
|
||||
};
|
||||
|
||||
|
||||
static struct android_pmem_platform_data android_pmem_vpu_pdata = {
|
||||
.name = "pmem_vpu",
|
||||
static struct vpu_mem_platform_data vpu_mem_pdata = {
|
||||
.name = "vpu_mem",
|
||||
.start = PMEM_VPU_BASE,
|
||||
.size = PMEM_VPU_SIZE,
|
||||
.no_allocator = 0,
|
||||
.cached = 1,
|
||||
};
|
||||
|
||||
static struct platform_device android_pmem_vpu_device = {
|
||||
.name = "android_pmem",
|
||||
.id = 2,
|
||||
static struct platform_device rk29_vpu_mem_device = {
|
||||
.name = "vpu_mem",
|
||||
.id = 2,
|
||||
.dev = {
|
||||
.platform_data = &android_pmem_vpu_pdata,
|
||||
.platform_data = &vpu_mem_pdata,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -733,7 +734,7 @@ static int rk29_sdmmc0_cfg_gpio(void)
|
||||
#define CONFIG_SDMMC0_USE_DMA
|
||||
struct rk29_sdmmc_platform_data default_sdmmc0_data = {
|
||||
.host_ocr_avail = (MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|
|
||||
MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33|
|
||||
MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33|
|
||||
MMC_VDD_33_34|MMC_VDD_34_35| MMC_VDD_35_36),
|
||||
.host_caps = (MMC_CAP_4_BIT_DATA|MMC_CAP_MMC_HIGHSPEED|MMC_CAP_SD_HIGHSPEED),
|
||||
.io_init = rk29_sdmmc0_cfg_gpio,
|
||||
@@ -816,7 +817,7 @@ static struct platform_device rk29_device_keys = {
|
||||
|
||||
static void __init rk29_board_iomux_init(void)
|
||||
{
|
||||
#ifdef CONFIG_UART0_RK29
|
||||
#ifdef CONFIG_UART0_RK29
|
||||
rk29_mux_api_set(GPIO1B7_UART0SOUT_NAME, GPIO1L_UART0_SOUT);
|
||||
rk29_mux_api_set(GPIO1B6_UART0SIN_NAME, GPIO1L_UART0_SIN);
|
||||
#ifdef CONFIG_UART0_CTS_RTS_RK29
|
||||
@@ -824,11 +825,11 @@ static void __init rk29_board_iomux_init(void)
|
||||
rk29_mux_api_set(GPIO1C0_UART0CTSN_SDMMC1DETECTN_NAME, GPIO1H_UART0_CTS_N);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_UART1_RK29
|
||||
#ifdef CONFIG_UART1_RK29
|
||||
rk29_mux_api_set(GPIO2A5_UART1SOUT_NAME, GPIO2L_UART1_SOUT);
|
||||
rk29_mux_api_set(GPIO2A4_UART1SIN_NAME, GPIO2L_UART1_SIN);
|
||||
#endif
|
||||
#ifdef CONFIG_UART2_RK29
|
||||
#ifdef CONFIG_UART2_RK29
|
||||
rk29_mux_api_set(GPIO2B1_UART2SOUT_NAME, GPIO2L_UART2_SOUT);
|
||||
rk29_mux_api_set(GPIO2B0_UART2SIN_NAME, GPIO2L_UART2_SIN);
|
||||
#ifdef CONFIG_UART2_CTS_RTS_RK29
|
||||
@@ -836,7 +837,7 @@ static void __init rk29_board_iomux_init(void)
|
||||
rk29_mux_api_set(GPIO2A6_UART2CTSN_NAME, GPIO2L_UART2_CTS_N);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_UART3_RK29
|
||||
#ifdef CONFIG_UART3_RK29
|
||||
rk29_mux_api_set(GPIO2B3_UART3SOUT_NAME, GPIO2L_UART3_SOUT);
|
||||
rk29_mux_api_set(GPIO2B2_UART3SIN_NAME, GPIO2L_UART3_SIN);
|
||||
#ifdef CONFIG_UART3_CTS_RTS_RK29
|
||||
@@ -906,7 +907,7 @@ static struct platform_device *devices[] __initdata = {
|
||||
&rk29_soc_camera_pdrv,
|
||||
#endif
|
||||
&android_pmem_device,
|
||||
&android_pmem_vpu_device,
|
||||
&rk29_vpu_mem_device,
|
||||
};
|
||||
|
||||
/*****************************************************************************************
|
||||
@@ -1152,12 +1153,12 @@ static void __init machine_rk29_mapio(void)
|
||||
{
|
||||
rk29_map_common_io();
|
||||
rk29_clock_init();
|
||||
rk29_iomux_init();
|
||||
rk29_iomux_init();
|
||||
}
|
||||
|
||||
MACHINE_START(RK29, "RK29board")
|
||||
/* UART for LL DEBUG */
|
||||
.phys_io = RK29_UART1_PHYS,
|
||||
.phys_io = RK29_UART1_PHYS,
|
||||
.io_pg_offst = ((RK29_UART1_BASE) >> 18) & 0xfffc,
|
||||
.boot_params = RK29_SDRAM_PHYS + 0x88000,
|
||||
.fixup = machine_rk29_fixup,
|
||||
|
||||
50
arch/arm/mach-rk29/include/mach/vpu_mem.h
Normal file
50
arch/arm/mach-rk29/include/mach/vpu_mem.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* arch/arm/mach-rk29/include/mach/vpu_mem.h
|
||||
*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ARCH_ARM_MACH_RK29_VPU_MEM_H
|
||||
#define __ARCH_ARM_MACH_RK29_VPU_MEM_H
|
||||
|
||||
|
||||
#define VPU_MEM_IOCTL_MAGIC 'p'
|
||||
#define VPU_MEM_GET_PHYS _IOW(VPU_MEM_IOCTL_MAGIC, 1, unsigned int)
|
||||
#define VPU_MEM_GET_TOTAL_SIZE _IOW(VPU_MEM_IOCTL_MAGIC, 2, unsigned int)
|
||||
#define VPU_MEM_ALLOCATE _IOW(VPU_MEM_IOCTL_MAGIC, 3, unsigned int)
|
||||
#define VPU_MEM_FREE _IOW(VPU_MEM_IOCTL_MAGIC, 4, unsigned int)
|
||||
#define VPU_MEM_CACHE_FLUSH _IOW(VPU_MEM_IOCTL_MAGIC, 5, unsigned int)
|
||||
#define VPU_MEM_DUPLICATE _IOW(VPU_MEM_IOCTL_MAGIC, 6, unsigned int)
|
||||
#define VPU_MEM_LINK _IOW(VPU_MEM_IOCTL_MAGIC, 7, unsigned int)
|
||||
|
||||
#define VPU_MEM_CONNECT _IOW(VPU_MEM_IOCTL_MAGIC, 8, unsigned int)
|
||||
#define VPU_MEM_MAP _IOW(VPU_MEM_IOCTL_MAGIC, 9, unsigned int)
|
||||
#define VPU_MEM_GET_SIZE _IOW(VPU_MEM_IOCTL_MAGIC, 10, unsigned int)
|
||||
#define VPU_MEM_UNMAP _IOW(VPU_MEM_IOCTL_MAGIC, 11, unsigned int)
|
||||
|
||||
struct vpu_mem_platform_data
|
||||
{
|
||||
const char* name;
|
||||
/* starting physical address of memory region */
|
||||
unsigned long start;
|
||||
/* size of memory region */
|
||||
unsigned long size;
|
||||
/* set to indicate maps of this region should be cached, if a mix of
|
||||
* cached and uncached is desired, set this and open the device with
|
||||
* O_SYNC to get an uncached region */
|
||||
unsigned cached;
|
||||
/* The MSM7k has bits to enable a write buffer in the bus controller*/
|
||||
unsigned buffered;
|
||||
};
|
||||
|
||||
#endif //__ARCH_ARM_MACH_RK29_VPU_MEM_H
|
||||
|
||||
818
arch/arm/mach-rk29/vpu_mem.c
Normal file
818
arch/arm/mach-rk29/vpu_mem.c
Normal file
@@ -0,0 +1,818 @@
|
||||
/* arch/arm/mach-rk29/vpu_mem.c
|
||||
*
|
||||
* Copyright (C) 2010 ROCKCHIP, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mempolicy.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <mach/vpu_mem.h>
|
||||
|
||||
|
||||
#define VPU_MEM_MAX_ORDER 128
|
||||
#define VPU_MEM_MIN_ALLOC PAGE_SIZE
|
||||
|
||||
#define VPU_MEM_DEBUG 1
|
||||
|
||||
#define VPU_MEM_BITMAP_ERR (-1)
|
||||
#define VPU_MEM_ERR_FREE_REFN_ERR (-5)
|
||||
|
||||
struct vpu_mem_data {
|
||||
/* protects this data field, if the mm_mmap sem will be held at the
|
||||
* same time as this sem, the mm sem must be taken first (as this is
|
||||
* the order for vma_open and vma_close ops */
|
||||
struct rw_semaphore sem;
|
||||
/* process id of teh mapping process */
|
||||
pid_t pid;
|
||||
/* a list of currently available regions if this is a suballocation */
|
||||
struct list_head region_list;
|
||||
/* a linked list of data so we can access them for debugging */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct vpu_mem_bits {
|
||||
unsigned short pfn; /* page frame number - vpu_mem space max 256M */
|
||||
signed refrn:7; /* reference number */
|
||||
unsigned first:1; /* 1 if first, 0 if not first */
|
||||
signed avail:7; /* available link number */
|
||||
unsigned allocated:1; /* 1 if allocated, 0 if free */
|
||||
};
|
||||
|
||||
struct vpu_mem_region {
|
||||
unsigned long offset;
|
||||
unsigned long len;
|
||||
};
|
||||
|
||||
struct vpu_mem_region_node {
|
||||
struct vpu_mem_region region;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define VPU_MEM_DEBUG_MSGS 0
|
||||
#if VPU_MEM_DEBUG_MSGS
|
||||
#define DLOG(fmt,args...) \
|
||||
do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
|
||||
##args); } \
|
||||
while (0)
|
||||
#else
|
||||
#define DLOG(x...) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct vpu_mem_info {
|
||||
struct miscdevice dev;
|
||||
/* physical start address of the remaped vpu_mem space */
|
||||
unsigned long base;
|
||||
/* vitual start address of the remaped vpu_mem space */
|
||||
unsigned char __iomem *vbase;
|
||||
/* total size of the vpu_mem space */
|
||||
unsigned long size;
|
||||
/* number of entries in the vpu_mem space */
|
||||
unsigned long num_entries;
|
||||
/* indicates maps of this region should be cached, if a mix of
|
||||
* cached and uncached is desired, set this and open the device with
|
||||
* O_SYNC to get an uncached region */
|
||||
unsigned cached;
|
||||
unsigned buffered;
|
||||
|
||||
/* no data_list is needed and no master mode is needed */
|
||||
/* for debugging, creates a list of vpu_mem file structs, the
|
||||
* data_list_sem should be taken before vpu_mem_data->sem if both are
|
||||
* needed */
|
||||
struct semaphore data_list_sem;
|
||||
struct list_head data_list;
|
||||
|
||||
/* the bitmap for the region indicating which entries are allocated
|
||||
* and which are free */
|
||||
struct vpu_mem_bits *bitmap;
|
||||
/* vpu_mem_sem protects the bitmap array
|
||||
* a write lock should be held when modifying entries in bitmap
|
||||
* a read lock should be held when reading data from bits or
|
||||
* dereferencing a pointer into bitmap
|
||||
*
|
||||
* vpu_mem_data->sem protects the vpu_mem data of a particular file
|
||||
* Many of the function that require the vpu_mem_data->sem have a non-
|
||||
* locking version for when the caller is already holding that sem.
|
||||
*
|
||||
* IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER:
|
||||
* down(vpu_mem_data->sem) => down(bitmap_sem)
|
||||
*/
|
||||
struct rw_semaphore bitmap_sem;
|
||||
};
|
||||
|
||||
static struct vpu_mem_info vpu_mem;
|
||||
static int vpu_mem_count;
|
||||
|
||||
#define VPU_MEM_IS_FREE(index) !(vpu_mem.bitmap[index].allocated)
|
||||
#define VPU_MEM_IS_FIRST(index) (vpu_mem.bitmap[index].first)
|
||||
#define VPU_MEM_BIT(index) &vpu_mem.bitmap[index]
|
||||
#define VPU_MEM_PFN(index) vpu_mem.bitmap[index].pfn
|
||||
#define VPU_MEM_NEXT_INDEX(index) (index + VPU_MEM_PFN(index))
|
||||
#define VPU_MEM_OFFSET(index) (index * VPU_MEM_MIN_ALLOC)
|
||||
#define VPU_MEM_START_ADDR(index) (VPU_MEM_OFFSET(index) + vpu_mem.base)
|
||||
#define VPU_MEM_SIZE(index) ((VPU_MEM_PFN(index)) * VPU_MEM_MIN_ALLOC)
|
||||
#define VPU_MEM_END_ADDR(index) (VPU_MEM_START_ADDR(index) + VPU_MEM_SIZE(index))
|
||||
#define VPU_MEM_START_VADDR(index) (VPU_MEM_OFFSET(index) + vpu_mem.vbase)
|
||||
#define VPU_MEM_END_VADDR(index) (VPU_MEM_START_VADDR(index) + VPU_MEM_SIZE(index))
|
||||
#define VPU_MEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
|
||||
|
||||
static int vpu_mem_release(struct inode *, struct file *);
|
||||
static int vpu_mem_mmap(struct file *, struct vm_area_struct *);
|
||||
static int vpu_mem_open(struct inode *, struct file *);
|
||||
static long vpu_mem_ioctl(struct file *, unsigned int, unsigned long);
|
||||
|
||||
struct file_operations vpu_mem_fops = {
|
||||
.open = vpu_mem_open,
|
||||
.mmap = vpu_mem_mmap,
|
||||
.unlocked_ioctl = vpu_mem_ioctl,
|
||||
.release = vpu_mem_release,
|
||||
};
|
||||
|
||||
int is_vpu_mem_file(struct file *file)
|
||||
{
|
||||
if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode))
|
||||
return 0;
|
||||
if (unlikely(file->f_dentry->d_inode->i_rdev !=
|
||||
MKDEV(MISC_MAJOR, vpu_mem.dev.minor)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void vpu_mem_get_region(struct vpu_mem_data *data,
|
||||
struct vpu_mem_region_node *region_node,
|
||||
int index, int pfn)
|
||||
{
|
||||
int curr, next = index + pfn;
|
||||
struct vpu_mem_bits *pbits;
|
||||
|
||||
if (VPU_MEM_IS_FREE(next)) {
|
||||
pbits = VPU_MEM_BIT(next);
|
||||
pbits->first = 1;
|
||||
pbits->pfn = VPU_MEM_PFN(index) - pfn;
|
||||
} else {
|
||||
if (!VPU_MEM_IS_FIRST(next))
|
||||
DLOG("something wrong when get_region pfn %d at index %d\n", pfn, index);
|
||||
}
|
||||
|
||||
pbits = VPU_MEM_BIT(index);
|
||||
|
||||
pbits->first = 1;
|
||||
pbits->pfn = pfn;
|
||||
pbits->refrn++;
|
||||
pbits->avail++;
|
||||
|
||||
for (curr = 0; curr < pfn; curr++)
|
||||
pbits[curr].allocated = 1;
|
||||
|
||||
region_node->region.offset = index;
|
||||
region_node->region.len = pfn;
|
||||
|
||||
down_write(&data->sem);
|
||||
list_add(®ion_node->list, &data->region_list);
|
||||
up_write(&data->sem);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
static int vpu_mem_put_region_by_index(struct vpu_mem_data *data, int index)
|
||||
{
|
||||
struct vpu_mem_bits *pbits = VPU_MEM_BIT(index);
|
||||
pbits->refrn--;
|
||||
pbits->avail--;
|
||||
|
||||
if (!pbits->avail)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < pbits->pfn; i++)
|
||||
pbits[i].allocated = 0;
|
||||
|
||||
down_write(&data->sem);
|
||||
{
|
||||
struct vpu_mem_region_node *region_node;
|
||||
struct list_head *elt, *elt2;
|
||||
list_for_each_safe(elt, elt2, &data->region_list) {
|
||||
region_node = list_entry(elt, struct vpu_mem_region_node, list);
|
||||
if (region_node->region.offset == index)
|
||||
{
|
||||
if (pbits->pfn != region_node->region.len)
|
||||
DLOG("something wrong when put_region at index %d\n", index);
|
||||
list_del(elt);
|
||||
kfree(region_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
up_write(&data->sem);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_mem_put_region_by_region(struct vpu_mem_region_node *region_node)
|
||||
{
|
||||
int index = region_node->region.offset;
|
||||
struct vpu_mem_bits *pbits = VPU_MEM_BIT(index);
|
||||
pbits->refrn--;
|
||||
pbits->avail--;
|
||||
|
||||
if (!pbits->avail)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < pbits->pfn; i++)
|
||||
pbits[i].allocated = 0;
|
||||
|
||||
list_del(®ion_node->list);
|
||||
kfree(region_node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vpu_mem_allocate(struct file *file, unsigned int len)
|
||||
{
|
||||
/* caller should hold the write lock on vpu_mem_sem! */
|
||||
/* return the corresponding pdata[] entry */
|
||||
int curr = 0;
|
||||
int end = vpu_mem.num_entries;
|
||||
int best_fit = -1;
|
||||
unsigned int pfn = (len + VPU_MEM_MIN_ALLOC - 1)/VPU_MEM_MIN_ALLOC;
|
||||
struct vpu_mem_data *data = (struct vpu_mem_data *)file->private_data;
|
||||
struct vpu_mem_region_node *region_node;
|
||||
|
||||
if (!is_vpu_mem_file(file)) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_INFO "allocate vpu_mem data from invalid file.\n");
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
DLOG("vpu_mem_allocate pfn %x\n", pfn);
|
||||
|
||||
region_node = kmalloc(sizeof(struct vpu_mem_region_node),
|
||||
GFP_KERNEL);
|
||||
if (!region_node) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_INFO "No space to allocate metadata!");
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* look through the bitmap:
|
||||
* if you find a free slot of the correct order use it
|
||||
* otherwise, use the best fit (smallest with size > order) slot
|
||||
*/
|
||||
while (curr < end) {
|
||||
if (VPU_MEM_IS_FREE(curr)) {
|
||||
if (VPU_MEM_PFN(curr) >= (unsigned char)pfn) {
|
||||
/* set the not free bit and clear others */
|
||||
best_fit = curr;
|
||||
printk("find fit size at index %d\n", curr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
curr = VPU_MEM_NEXT_INDEX(curr);
|
||||
}
|
||||
|
||||
/* if best_fit < 0, there are no suitable slots,
|
||||
* return an error
|
||||
*/
|
||||
if (best_fit < 0) {
|
||||
printk("vpu_mem: no space left to allocate!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DLOG("best_fit: %d next: %u\n", best_fit, best_fit + pfn);
|
||||
|
||||
vpu_mem_get_region(data, region_node, best_fit, pfn);
|
||||
|
||||
return best_fit;
|
||||
}
|
||||
|
||||
static int vpu_mem_free(struct file *file, int index)
|
||||
{
|
||||
/* caller should hold the write lock on vpu_mem_sem! */
|
||||
struct vpu_mem_bits *pbits = VPU_MEM_BIT(index);
|
||||
struct vpu_mem_data *data = (struct vpu_mem_data *)file->private_data;
|
||||
|
||||
if (!is_vpu_mem_file(file)) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_INFO "free vpu_mem data from invalid file.\n");
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
DLOG("free index %d\n", index);
|
||||
|
||||
if ((!pbits->first) ||
|
||||
(!pbits->allocated) ||
|
||||
((pbits->refrn - 1) < 0) ||
|
||||
((pbits->avail - 1) < 0))
|
||||
{
|
||||
DLOG("VPM ERR: found error in vpu_mem_free :\nvpu_mem.bitmap[%d].first %d, allocated %d, avail %d, refrn %d\n",
|
||||
index, pbits->first, pbits->allocated, pbits->avail, pbits->refrn);
|
||||
return VPU_MEM_BITMAP_ERR;
|
||||
}
|
||||
|
||||
return vpu_mem_put_region_by_index(data, index);
|
||||
}
|
||||
|
||||
static long vpu_mem_duplicate(struct file *file, int index)
|
||||
{
|
||||
/* caller should hold the write lock on vpu_mem_sem! */
|
||||
struct vpu_mem_bits *pbits = VPU_MEM_BIT(index);
|
||||
|
||||
if (!is_vpu_mem_file(file)) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_INFO "duplicate vpu_mem data from invalid file.\n");
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
DLOG("duplicate index %d\n", index);
|
||||
|
||||
if ((!pbits->first) ||
|
||||
(!pbits->allocated) ||
|
||||
(!pbits->avail))
|
||||
{
|
||||
DLOG("VPM ERR: found error in vpu_mem_duplicate :\nvpu_mem.bitmap[%d].first %d, allocated %d, avail %d, refrn %d\n",
|
||||
index, pbits->first, pbits->allocated, pbits->avail, pbits->refrn);
|
||||
return VPU_MEM_BITMAP_ERR;
|
||||
}
|
||||
|
||||
pbits->avail++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vpu_mem_link(struct file *file, int index)
|
||||
{
|
||||
struct vpu_mem_bits *pbits = VPU_MEM_BIT(index);
|
||||
struct vpu_mem_data *data = (struct vpu_mem_data *)file->private_data;
|
||||
struct vpu_mem_region_node *region_node;
|
||||
|
||||
if (!is_vpu_mem_file(file)) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_INFO "link vpu_mem data from invalid file.\n");
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
region_node = kmalloc(sizeof(struct vpu_mem_region_node),
|
||||
GFP_KERNEL);
|
||||
if (!region_node) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_INFO "No space to allocate metadata!");
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* caller should hold the write lock on vpu_mem_sem! */
|
||||
DLOG("link index %d\n", index);
|
||||
|
||||
if ((!pbits->first) ||
|
||||
(!pbits->allocated) ||
|
||||
(!pbits->avail) ||
|
||||
(pbits->avail <= pbits->refrn))
|
||||
{
|
||||
DLOG("VPM ERR: found error in vpu_mem_duplicate :\nvpu_mem.bitmap[%d].first %d, allocated %d, avail %d, refrn %d\n",
|
||||
index, pbits->first, pbits->allocated, pbits->avail, pbits->refrn);
|
||||
return VPU_MEM_BITMAP_ERR;
|
||||
}
|
||||
|
||||
pbits->refrn++;
|
||||
|
||||
region_node->region.offset = index;
|
||||
region_node->region.len = pbits->pfn;
|
||||
|
||||
down_write(&data->sem);
|
||||
list_add(®ion_node->list, &data->region_list);
|
||||
up_write(&data->sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vpu_mem_flush(struct file *file, long index)
|
||||
{
|
||||
struct vpu_mem_data *data;
|
||||
void *flush_start, *flush_end;
|
||||
|
||||
if (!is_vpu_mem_file(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = (struct vpu_mem_data *)file->private_data;
|
||||
if (!vpu_mem.cached || file->f_flags & O_SYNC)
|
||||
return;
|
||||
|
||||
down_read(&data->sem);
|
||||
flush_start = VPU_MEM_START_VADDR(index);
|
||||
flush_end = VPU_MEM_END_VADDR(index);
|
||||
dmac_flush_range(flush_start, flush_end);
|
||||
up_read(&data->sem);
|
||||
}
|
||||
|
||||
static pgprot_t phys_mem_access_prot(struct file *file, pgprot_t vma_prot)
|
||||
{
|
||||
#ifdef pgprot_noncached
|
||||
if (vpu_mem.cached == 0 || file->f_flags & O_SYNC)
|
||||
return pgprot_noncached(vma_prot);
|
||||
#endif
|
||||
#ifdef pgprot_ext_buffered
|
||||
else if (vpu_mem.buffered)
|
||||
return pgprot_ext_buffered(vma_prot);
|
||||
#endif
|
||||
return vma_prot;
|
||||
}
|
||||
|
||||
static int vpu_mem_map_pfn_range(struct vm_area_struct *vma, unsigned long len)
|
||||
{
|
||||
DLOG("map len %lx\n", len);
|
||||
BUG_ON(!VPU_MEM_IS_PAGE_ALIGNED(vma->vm_start));
|
||||
BUG_ON(!VPU_MEM_IS_PAGE_ALIGNED(vma->vm_end));
|
||||
BUG_ON(!VPU_MEM_IS_PAGE_ALIGNED(len));
|
||||
if (io_remap_pfn_range(vma, vma->vm_start,
|
||||
vpu_mem.base >> PAGE_SHIFT,
|
||||
len, vma->vm_page_prot)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_mem_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct vpu_mem_data *data;
|
||||
int ret = 0;
|
||||
|
||||
DLOG("current %u file %p(%d)\n", current->pid, file, (int)file_count(file));
|
||||
/* setup file->private_data to indicate its unmapped */
|
||||
/* you can only open a vpu_mem device one time */
|
||||
if (file->private_data != NULL)
|
||||
return -1;
|
||||
data = kmalloc(sizeof(struct vpu_mem_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
printk("vpu_mem: unable to allocate memory for vpu_mem metadata.");
|
||||
return -1;
|
||||
}
|
||||
data->pid = 0;
|
||||
|
||||
INIT_LIST_HEAD(&data->region_list);
|
||||
init_rwsem(&data->sem);
|
||||
|
||||
file->private_data = data;
|
||||
INIT_LIST_HEAD(&data->list);
|
||||
|
||||
down(&vpu_mem.data_list_sem);
|
||||
list_add(&data->list, &vpu_mem.data_list);
|
||||
up(&vpu_mem.data_list_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_mem_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct vpu_mem_data *data;
|
||||
unsigned long vma_size = vma->vm_end - vma->vm_start;
|
||||
int ret = 0;
|
||||
|
||||
if (vma->vm_pgoff || !VPU_MEM_IS_PAGE_ALIGNED(vma_size)) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_ERR "vpu_mem: mmaps must be at offset zero, aligned"
|
||||
" and a multiple of pages_size.\n");
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = (struct vpu_mem_data *)file->private_data;
|
||||
|
||||
printk(KERN_ALERT "file->private_data : 0x%x\n", (unsigned int)data);
|
||||
|
||||
down_write(&data->sem);
|
||||
|
||||
/* assert: vma_size must be the total size of the vpu_mem */
|
||||
if (vpu_mem.size != vma_size) {
|
||||
#if VPU_MEM_DEBUG
|
||||
printk(KERN_WARNING "vpu_mem: mmap size [%lu] does not match"
|
||||
"size of backing region [%lu].\n", vma_size, vpu_mem.size);
|
||||
#endif
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
vma->vm_pgoff = vpu_mem.base >> PAGE_SHIFT;
|
||||
vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_page_prot);
|
||||
|
||||
if (vpu_mem_map_pfn_range(vma, vma_size)) {
|
||||
printk(KERN_INFO "vpu_mem: mmap failed in kernel!\n");
|
||||
ret = -EAGAIN;
|
||||
goto error;
|
||||
}
|
||||
|
||||
data->pid = current->pid;
|
||||
|
||||
error:
|
||||
up_write(&data->sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_mem_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct vpu_mem_data *data = (struct vpu_mem_data *)file->private_data;
|
||||
struct list_head *elt, *elt2;
|
||||
|
||||
down(&vpu_mem.data_list_sem);
|
||||
list_del(&data->list);
|
||||
up(&vpu_mem.data_list_sem);
|
||||
|
||||
down_write(&data->sem);
|
||||
file->private_data = NULL;
|
||||
list_for_each_safe(elt, elt2, &data->region_list) {
|
||||
struct vpu_mem_region_node *region_node = list_entry(elt, struct vpu_mem_region_node, list);
|
||||
vpu_mem_put_region_by_region(region_node);
|
||||
}
|
||||
BUG_ON(!list_empty(&data->region_list));
|
||||
up_write(&data->sem);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vpu_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long index, ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case VPU_MEM_GET_PHYS:
|
||||
DLOG("get_phys\n");
|
||||
printk(KERN_INFO "vpu_mem: request for physical address of vpu_mem region "
|
||||
"from process %d.\n", current->pid);
|
||||
if (copy_to_user((void __user *)arg, &vpu_mem.base, sizeof(vpu_mem.base)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case VPU_MEM_GET_TOTAL_SIZE:
|
||||
DLOG("get total size\n");
|
||||
if (copy_to_user((void __user *)arg, &vpu_mem.size, sizeof(vpu_mem.size)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case VPU_MEM_ALLOCATE:
|
||||
DLOG("allocate\n");
|
||||
{
|
||||
unsigned int size;
|
||||
if (copy_from_user(&size, (void __user *)arg, sizeof(size)))
|
||||
return -EFAULT;
|
||||
down_write(&vpu_mem.bitmap_sem);
|
||||
index = vpu_mem_allocate(file, size);
|
||||
up_write(&vpu_mem.bitmap_sem);
|
||||
DLOG("allocate at index %ld\n", index);
|
||||
return index;
|
||||
break;
|
||||
}
|
||||
case VPU_MEM_FREE:
|
||||
{
|
||||
DLOG("mem free\n");
|
||||
if (copy_from_user(&index, (void __user *)arg, sizeof(index)))
|
||||
return -EFAULT;
|
||||
if (index >= vpu_mem.size)
|
||||
return -EACCES;
|
||||
down_write(&vpu_mem.bitmap_sem);
|
||||
ret = vpu_mem_free(file, index);
|
||||
up_write(&vpu_mem.bitmap_sem);
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case VPU_MEM_CACHE_FLUSH:
|
||||
{
|
||||
DLOG("flush\n");
|
||||
if (copy_from_user(&index, (void __user *)arg, sizeof(index)))
|
||||
return -EFAULT;
|
||||
|
||||
down_write(&vpu_mem.bitmap_sem);
|
||||
vpu_mem_flush(file, index);
|
||||
up_write(&vpu_mem.bitmap_sem);
|
||||
break;
|
||||
}
|
||||
case VPU_MEM_DUPLICATE:
|
||||
{
|
||||
DLOG("duplicate\n");
|
||||
if (copy_from_user(&index, (void __user *)arg, sizeof(index)))
|
||||
return -EFAULT;
|
||||
down_write(&vpu_mem.bitmap_sem);
|
||||
ret = vpu_mem_duplicate(file, index);
|
||||
up_write(&vpu_mem.bitmap_sem);
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case VPU_MEM_LINK:
|
||||
{
|
||||
DLOG("link\n");
|
||||
if (copy_from_user(&index, (void __user *)arg, sizeof(index)))
|
||||
return -EFAULT;
|
||||
down_write(&vpu_mem.bitmap_sem);
|
||||
ret = vpu_mem_link(file, index);
|
||||
up_write(&vpu_mem.bitmap_sem);
|
||||
break;
|
||||
}
|
||||
case VPU_MEM_MAP:
|
||||
DLOG("map\n");
|
||||
break;
|
||||
case VPU_MEM_CONNECT:
|
||||
DLOG("connect\n");
|
||||
break;
|
||||
case VPU_MEM_GET_SIZE:
|
||||
DLOG("get_size\n");
|
||||
break;
|
||||
case VPU_MEM_UNMAP:
|
||||
DLOG("unmap\n");
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VPU_MEM_DEBUG
|
||||
static ssize_t debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t debug_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct list_head *elt, *elt2;
|
||||
struct vpu_mem_data *data;
|
||||
struct vpu_mem_region_node *region_node;
|
||||
const int debug_bufmax = 4096;
|
||||
static char buffer[4096];
|
||||
int n = 0;
|
||||
|
||||
DLOG("debug open\n");
|
||||
n = scnprintf(buffer, debug_bufmax,
|
||||
"pid #: mapped regions (offset, len) (offset,len)...\n");
|
||||
|
||||
down(&vpu_mem.data_list_sem);
|
||||
list_for_each(elt, &vpu_mem.data_list) {
|
||||
data = list_entry(elt, struct vpu_mem_data, list);
|
||||
down_read(&data->sem);
|
||||
n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:",
|
||||
data->pid);
|
||||
list_for_each(elt2, &data->region_list) {
|
||||
region_node = list_entry(elt2, struct vpu_mem_region_node,
|
||||
list);
|
||||
n += scnprintf(buffer + n, debug_bufmax - n,
|
||||
"(%lx,%lx) ",
|
||||
region_node->region.offset,
|
||||
region_node->region.len);
|
||||
}
|
||||
n += scnprintf(buffer + n, debug_bufmax - n, "\n");
|
||||
up_read(&data->sem);
|
||||
}
|
||||
up(&vpu_mem.data_list_sem);
|
||||
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static struct file_operations debug_fops = {
|
||||
.read = debug_read,
|
||||
.open = debug_open,
|
||||
};
|
||||
#endif
|
||||
|
||||
int vpu_mem_setup(struct vpu_mem_platform_data *pdata)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (vpu_mem_count)
|
||||
{
|
||||
printk(KERN_ALERT "Only one vpu_mem driver can be register!\n");
|
||||
goto err_cant_register_device;
|
||||
}
|
||||
|
||||
memset(&vpu_mem, 0, sizeof(struct vpu_mem_info));
|
||||
|
||||
vpu_mem.cached = pdata->cached;
|
||||
vpu_mem.buffered = pdata->buffered;
|
||||
vpu_mem.base = pdata->start;
|
||||
vpu_mem.size = pdata->size;
|
||||
init_rwsem(&vpu_mem.bitmap_sem);
|
||||
init_MUTEX(&vpu_mem.data_list_sem);
|
||||
INIT_LIST_HEAD(&vpu_mem.data_list);
|
||||
vpu_mem.dev.name = pdata->name;
|
||||
vpu_mem.dev.minor = MISC_DYNAMIC_MINOR;
|
||||
vpu_mem.dev.fops = &vpu_mem_fops;
|
||||
|
||||
err = misc_register(&vpu_mem.dev);
|
||||
if (err) {
|
||||
printk(KERN_ALERT "Unable to register vpu_mem driver!\n");
|
||||
goto err_cant_register_device;
|
||||
}
|
||||
printk(KERN_ALERT "%s: %d init\n", pdata->name, vpu_mem.dev.minor);
|
||||
vpu_mem_count++;
|
||||
|
||||
vpu_mem.num_entries = vpu_mem.size / VPU_MEM_MIN_ALLOC;
|
||||
vpu_mem.bitmap = kmalloc(vpu_mem.num_entries *
|
||||
sizeof(struct vpu_mem_bits), GFP_KERNEL);
|
||||
if (!vpu_mem.bitmap)
|
||||
goto err_no_mem_for_metadata;
|
||||
|
||||
memset(vpu_mem.bitmap, 0, sizeof(struct vpu_mem_bits) *
|
||||
vpu_mem.num_entries);
|
||||
|
||||
/* record the total page number */
|
||||
vpu_mem.bitmap[0].pfn = vpu_mem.num_entries;
|
||||
|
||||
if (vpu_mem.cached)
|
||||
vpu_mem.vbase = ioremap_cached(vpu_mem.base,
|
||||
vpu_mem.size);
|
||||
#ifdef ioremap_ext_buffered
|
||||
else if (vpu_mem.buffered)
|
||||
vpu_mem.vbase = ioremap_ext_buffered(vpu_mem.base,
|
||||
vpu_mem.size);
|
||||
#endif
|
||||
else
|
||||
vpu_mem.vbase = ioremap(vpu_mem.base, vpu_mem.size);
|
||||
|
||||
if (vpu_mem.vbase == 0)
|
||||
goto error_cant_remap;
|
||||
|
||||
#if VPU_MEM_DEBUG
|
||||
debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)vpu_mem.dev.minor,
|
||||
&debug_fops);
|
||||
#endif
|
||||
return 0;
|
||||
error_cant_remap:
|
||||
kfree(vpu_mem.bitmap);
|
||||
err_no_mem_for_metadata:
|
||||
misc_deregister(&vpu_mem.dev);
|
||||
err_cant_register_device:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vpu_mem_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vpu_mem_platform_data *pdata;
|
||||
|
||||
if (!pdev || !pdev->dev.platform_data) {
|
||||
printk(KERN_ALERT "Unable to probe vpu_mem!\n");
|
||||
return -1;
|
||||
}
|
||||
pdata = pdev->dev.platform_data;
|
||||
return vpu_mem_setup(pdata);
|
||||
}
|
||||
|
||||
static int vpu_mem_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (!pdev || !pdev->dev.platform_data) {
|
||||
printk(KERN_ALERT "Unable to remove vpu_mem!\n");
|
||||
return -1;
|
||||
}
|
||||
if (vpu_mem_count) {
|
||||
misc_deregister(&vpu_mem.dev);
|
||||
vpu_mem_count--;
|
||||
} else {
|
||||
printk(KERN_ALERT "no vpu_mem to remove!\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver vpu_mem_driver = {
|
||||
.probe = vpu_mem_probe,
|
||||
.remove = vpu_mem_remove,
|
||||
.driver = { .name = "vpu_mem" }
|
||||
};
|
||||
|
||||
|
||||
static int __init vpu_mem_init(void)
|
||||
{
|
||||
return platform_driver_register(&vpu_mem_driver);
|
||||
}
|
||||
|
||||
static void __exit vpu_mem_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&vpu_mem_driver);
|
||||
}
|
||||
|
||||
module_init(vpu_mem_init);
|
||||
module_exit(vpu_mem_exit);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user