From 8e4bff4cab0ddac6060645b0715210484d02ff40 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Wed, 30 Aug 2017 08:25:59 +0000 Subject: Initial file dump from open source release --- drivers/misc/sprd_coda7l/Kconfig | 5 + drivers/misc/sprd_coda7l/Makefile | 4 + .../TShark2_CODEC_AHB_Control_Register.h | 58 + drivers/misc/sprd_coda7l/vmm.h | 702 +++++++++ drivers/misc/sprd_coda7l/vpu.c | 1597 ++++++++++++++++++++ drivers/misc/sprd_coda7l/vpu.h | 113 ++ drivers/misc/sprd_coda7l/vpuconfig.h | 163 ++ 7 files changed, 2642 insertions(+) create mode 100644 drivers/misc/sprd_coda7l/Kconfig create mode 100644 drivers/misc/sprd_coda7l/Makefile create mode 100644 drivers/misc/sprd_coda7l/TShark2_CODEC_AHB_Control_Register.h create mode 100644 drivers/misc/sprd_coda7l/vmm.h create mode 100644 drivers/misc/sprd_coda7l/vpu.c create mode 100644 drivers/misc/sprd_coda7l/vpu.h create mode 100644 drivers/misc/sprd_coda7l/vpuconfig.h (limited to 'drivers/misc/sprd_coda7l') diff --git a/drivers/misc/sprd_coda7l/Kconfig b/drivers/misc/sprd_coda7l/Kconfig new file mode 100644 index 00000000..bb2eba44 --- /dev/null +++ b/drivers/misc/sprd_coda7l/Kconfig @@ -0,0 +1,5 @@ +# to add sprd vsp configs in the future +config SPRD_CODA7L + bool "sprd coda7l driver" + help + "enable coda7l device driver" diff --git a/drivers/misc/sprd_coda7l/Makefile b/drivers/misc/sprd_coda7l/Makefile new file mode 100644 index 00000000..61878093 --- /dev/null +++ b/drivers/misc/sprd_coda7l/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_SPRD_CODA7L) += vpu.o + + diff --git a/drivers/misc/sprd_coda7l/TShark2_CODEC_AHB_Control_Register.h b/drivers/misc/sprd_coda7l/TShark2_CODEC_AHB_Control_Register.h new file mode 100644 index 00000000..14235fd7 --- /dev/null +++ b/drivers/misc/sprd_coda7l/TShark2_CODEC_AHB_Control_Register.h @@ -0,0 +1,58 @@ +/* the head file modifier: ang 2015-01-21 14:34:02*/ + +/* +* Copyright (C) 2013 Spreadtrum Communications Inc. +* +* 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. +* +************************************************* +* Automatically generated C header: do not edit * +************************************************* +*/ + +#ifndef __SCI_GLB_REGS_H__ +#error "Don't include this file directly, Pls include sci_glb_regs.h" +#endif + + +#ifndef __H_REGS_CODEC_AHB_HEADFILE_H__ +#define __H_REGS_CODEC_AHB_HEADFILE_H__ __FILE__ + +#define REGS_CODEC_AHB + +/* registers definitions for CODEC_AHB */ +#define REG_CODEC_AHB_AHB_RST SCI_ADDR(REGS_CODEC_AHB_BASE, 0x0000)/*AHB_RST*/ +#define REG_CODEC_AHB_CKG_ENABLE SCI_ADDR(REGS_CODEC_AHB_BASE, 0x0004)/*CKG_ENABLE*/ +#define REG_CODEC_AHB_CLOCK_SEL SCI_ADDR(REGS_CODEC_AHB_BASE, 0x0008)/*CLOCK_SEL*/ +#define REG_CODEC_AHB_CODA7_STAT SCI_ADDR(REGS_CODEC_AHB_BASE, 0x000C)/*CODA7_STAT*/ + + + +/* bits definitions for register REG_CODEC_AHB_AHB_RST */ +#define BIT_CODA7_APB_SOFT_RST ( BIT(2) ) +#define BIT_CODA7_CC_SOFT_RST ( BIT(1) ) +#define BIT_CODA7_AXI_SOFT_RST ( BIT(0) ) + +/* bits definitions for register REG_CODEC_AHB_CKG_ENABLE */ +#define BIT_CODA7_CKG_EN ( BIT(4) ) +#define BIT_CODA7_CC_CKG_EN ( BIT(1) ) +#define BIT_CODA7_AXI_CKG_EN ( BIT(0) ) + +/* bits definitions for register REG_CODEC_AHB_CLOCK_SEL */ +#define BITS_CLK_CODA7_AXI_SEL(_X_) ( (_X_) << 8 & (BIT(8)|BIT(9)) ) +#define BITS_CLK_CODA7_CC_SEL(_X_) ( (_X_) << 4 & (BIT(4)|BIT(5)) ) +#define BITS_CLK_CODA7_APB_SEL(_X_) ( (_X_) & (BIT(0)|BIT(1)) ) + +/* bits definitions for register REG_CODEC_AHB_CODA7_STAT */ +#define BIT_CODA7_RUN ( BIT(1) ) +#define BIT_CODA7_IDLE ( BIT(0) ) + +#endif diff --git a/drivers/misc/sprd_coda7l/vmm.h b/drivers/misc/sprd_coda7l/vmm.h new file mode 100644 index 00000000..a7a1316a --- /dev/null +++ b/drivers/misc/sprd_coda7l/vmm.h @@ -0,0 +1,702 @@ +/** + vmm.h + + memory allocator for VPU + + Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library 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 Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef __CNM_VIDEO_MEMORY_MANAGEMENT_H__ +#define __CNM_VIDEO_MEMORY_MANAGEMENT_H__ + + +typedef struct _video_mm_info_struct { + unsigned long total_pages; + unsigned long alloc_pages; + unsigned long free_pages; + unsigned long page_size; +} vmem_info_t; + +typedef unsigned long long vmem_key_t; + +#define VMEM_PAGE_SIZE (16*1024) +#define MAKE_KEY(_a, _b) (((vmem_key_t)_a)<<32 | _b) +#define KEY_TO_VALUE(_key) (_key>>32) + +typedef struct page_struct { + int pageno; + unsigned long addr; + int used; + int alloc_pages; + int first_pageno; +} page_t; + + +typedef struct avl_node_struct { + vmem_key_t key; + int height; + page_t* page; + struct avl_node_struct* left; + struct avl_node_struct* right; +} avl_node_t; + + +typedef struct _video_mm_struct { + avl_node_t* free_tree; + avl_node_t* alloc_tree; + page_t* page_list; + int num_pages; + unsigned long base_addr; + unsigned long mem_size; + int free_page_count; + int alloc_page_count; +} video_mm_t; + + + +#define VMEM_P_ALLOC(_x) kmalloc(_x, GFP_KERNEL) +#define VMEM_P_FREE(_x) kfree(_x) + +#define VMEM_ASSERT(_exp) if (!(_exp)) { printk(KERN_INFO "VMEM_ASSERT at %s:%d\n", __FILE__, __LINE__); /*while(1);*/ } +#define VMEM_HEIGHT(_tree) (_tree==NULL ? -1 : _tree->height) + + +#define MAX(_a, _b) (_a >= _b ? _a : _b) + +typedef enum { + LEFT, + RIGHT +} rotation_dir_t; + + + +typedef struct avl_node_data_struct { + int key; + page_t* page; +} avl_node_data_t; + +static avl_node_t* +make_avl_node( + vmem_key_t key, + page_t* page +) +{ + avl_node_t* node = (avl_node_t*)VMEM_P_ALLOC(sizeof(avl_node_t)); + node->key = key; + node->page = page; + node->height = 0; + node->left = NULL; + node->right = NULL; + + return node; +} + + +static int +get_balance_factor( + avl_node_t* tree +) +{ + int factor = 0; + if (tree) { + factor = VMEM_HEIGHT(tree->right) - VMEM_HEIGHT(tree->left); + } + + return factor; +} + +/* + * Left Rotation + * + * A B + * \ / \ + * B => A C + * / \ \ + * D C D + * + */ +static avl_node_t* +rotation_left( + avl_node_t* tree +) +{ + avl_node_t* rchild; + avl_node_t* lchild; + + if (tree == NULL) return NULL; + + rchild = tree->right; + if (rchild == NULL) { + return tree; + } + + lchild = rchild->left; + rchild->left = tree; + tree->right = lchild; + + tree->height = MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + rchild->height = MAX(VMEM_HEIGHT(rchild->left), VMEM_HEIGHT(rchild->right)) + 1; + + return rchild; +} + + +/* + * Reft Rotation + * + * A B + * \ / \ + * B => D A + * / \ / + * D C C + * + */ +static avl_node_t* +rotation_right( + avl_node_t* tree +) +{ + avl_node_t* rchild; + avl_node_t* lchild; + + if (tree == NULL) return NULL; + + lchild = tree->left; + if (lchild == NULL) return NULL; + + rchild = lchild->right; + lchild->right = tree; + tree->left = rchild; + + tree->height = MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + lchild->height = MAX(VMEM_HEIGHT(lchild->left), VMEM_HEIGHT(lchild->right)) + 1; + + return lchild; +} + +static avl_node_t* +do_balance( + avl_node_t* tree +) +{ + int bfactor = 0, child_bfactor; /* balancing factor */ + + bfactor = get_balance_factor(tree); + + if (bfactor >= 2) { + child_bfactor = get_balance_factor(tree->right); + if (child_bfactor == 1 || child_bfactor == 0) { + tree = rotation_left(tree); + } else if (child_bfactor == -1) { + tree->right = rotation_right(tree->right); + tree = rotation_left(tree); + } else { + printk(KERN_INFO "invalid balancing factor: %d\n", child_bfactor); + VMEM_ASSERT(0); + return NULL; + } + } else if (bfactor <= -2) { + child_bfactor = get_balance_factor(tree->left); + if (child_bfactor == -1 || child_bfactor == 0) { + tree = rotation_right(tree); + } else if (child_bfactor == 1) { + tree->left = rotation_left(tree->left); + tree = rotation_right(tree); + } else { + printk(KERN_INFO "invalid balancing factor: %d\n", child_bfactor); + VMEM_ASSERT(0); + return NULL; + } + } + + return tree; +} +static avl_node_t* +unlink_end_node( + avl_node_t* tree, + int dir, + avl_node_t** found_node +) +{ + avl_node_t* node; + *found_node = NULL; + + if (tree == NULL) return NULL; + + if (dir == LEFT) { + if (tree->left == NULL) { + *found_node = tree; + return NULL; + } + } else { + if (tree->right == NULL) { + *found_node = tree; + return NULL; + } + } + + if (dir == LEFT) { + node = tree->left; + tree->left = unlink_end_node(tree->left, LEFT, found_node); + if (tree->left == NULL) { + tree->left = (*found_node)->right; + (*found_node)->left = NULL; + (*found_node)->right = NULL; + } + } else { + node = tree->right; + tree->right = unlink_end_node(tree->right, RIGHT, found_node); + if (tree->right == NULL) { + tree->right = (*found_node)->left; + (*found_node)->left = NULL; + (*found_node)->right = NULL; + } + } + + tree->height = MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + + return do_balance(tree); +} + + +static avl_node_t* +avltree_insert( + avl_node_t* tree, + vmem_key_t key, + page_t* page +) +{ + if (tree == NULL) { + tree = make_avl_node(key, page); + } else { + if (key >= tree->key) { + tree->right = avltree_insert(tree->right, key, page); + } else { + tree->left = avltree_insert(tree->left, key, page); + } + } + + tree = do_balance(tree); + + tree->height = MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + + return tree; +} + +static avl_node_t* +do_unlink( + avl_node_t* tree +) +{ + avl_node_t* node; + avl_node_t* end_node; + node = unlink_end_node(tree->right, LEFT, &end_node); + if (node) { + tree->right = node; + } else { + node = unlink_end_node(tree->left, RIGHT, &end_node); + if (node) tree->left = node; + } + + if (node == NULL) { + node = tree->right ? tree->right : tree->left; + end_node = node; + } + + if (end_node) { + end_node->left = (tree->left != end_node) ? tree->left : end_node->left; + end_node->right = (tree->right != end_node) ? tree->right : end_node->right; + end_node->height = MAX(VMEM_HEIGHT(end_node->left), VMEM_HEIGHT(end_node->right)) + 1; + } + + tree = end_node; + + return tree; +} + +static avl_node_t* +avltree_remove( + avl_node_t* tree, + avl_node_t** found_node, + vmem_key_t key +) +{ + *found_node = NULL; + if (tree == NULL) { + printk(KERN_INFO "failed to find key %d\n", (int)key); + return NULL; + } + + if (key == tree->key) { + *found_node = tree; + tree = do_unlink(tree); + } else if (key > tree->key) { + tree->right = avltree_remove(tree->right, found_node, key); + } else { + tree->left = avltree_remove(tree->left, found_node, key); + } + + if (tree) tree->height = MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + + tree = do_balance(tree); + + return tree; +} + +void +avltree_free( + avl_node_t* tree +) +{ + if (tree == NULL) return; + if (tree->left == NULL && tree->right == NULL) { + VMEM_P_FREE(tree); + return; + } + + avltree_free(tree->left); + tree->left = NULL; + avltree_free(tree->right); + tree->right = NULL; +} + + + + +static avl_node_t* +remove_approx_value( + avl_node_t* tree, + avl_node_t** found, + vmem_key_t key +) +{ + *found = NULL; + if (tree == NULL) { + return NULL; + } + + if (key == tree->key) { + *found = tree; + tree = do_unlink(tree); + } else if (key > tree->key) { + tree->right = remove_approx_value(tree->right, found, key); + } else { + tree->left = remove_approx_value(tree->left, found, key); + if (*found == NULL) { + *found = tree; + tree = do_unlink(tree); + } + } + if (tree) tree->height = MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + tree = do_balance(tree); + + return tree; +} + +static void +set_blocks_free( + video_mm_t *mm, + int pageno, + int npages +) +{ + int last_pageno = pageno + npages - 1; + int i; + page_t* page; + page_t* last_page; + + VMEM_ASSERT(npages); + + if (last_pageno >= mm->num_pages) { + printk(KERN_INFO "set_blocks_free: invalid last page number: %d\n", last_pageno); + VMEM_ASSERT(0); + return; + } + + for (i=pageno; i<=last_pageno; i++) { + mm->page_list[i].used = 0; + mm->page_list[i].alloc_pages = 0; + mm->page_list[i].first_pageno = -1; + } + + page = &mm->page_list[pageno]; + page->alloc_pages = npages; + last_page = &mm->page_list[last_pageno]; + last_page->first_pageno = pageno; + + mm->free_tree = avltree_insert(mm->free_tree, MAKE_KEY(npages, pageno), page); +} + +static void +set_blocks_alloc( + video_mm_t *mm, + int pageno, + int npages +) +{ + int last_pageno = pageno + npages - 1; + int i; + page_t* page; + page_t* last_page; + + if (last_pageno >= mm->num_pages) { + printk(KERN_INFO "set_blocks_free: invalid last page number: %d\n", last_pageno); + VMEM_ASSERT(0); + return; + } + + for (i=pageno; i<=last_pageno; i++) { + mm->page_list[i].used = 1; + mm->page_list[i].alloc_pages = 0; + mm->page_list[i].first_pageno = -1; + } + + page = &mm->page_list[pageno]; + page->alloc_pages = npages; + + last_page = &mm->page_list[last_pageno]; + last_page->first_pageno = pageno; + + mm->alloc_tree = avltree_insert(mm->alloc_tree, MAKE_KEY(page->addr, 0), page); +} + + +int +vmem_init( + video_mm_t* mm, + unsigned long addr, + unsigned long size +) +{ + int i; + + if (NULL == mm) + return -1; + + mm->base_addr = (addr+(VMEM_PAGE_SIZE-1))&~(VMEM_PAGE_SIZE-1); + mm->mem_size = size&~VMEM_PAGE_SIZE; + mm->num_pages = mm->mem_size/VMEM_PAGE_SIZE; + mm->page_list = (page_t*)VMEM_P_ALLOC(mm->num_pages*sizeof(page_t)); + mm->free_tree = NULL; + mm->alloc_tree = NULL; + mm->free_page_count = mm->num_pages; + mm->alloc_page_count = 0; + + for (i=0; inum_pages; i++) { + mm->page_list[i].pageno = i; + mm->page_list[i].addr = mm->base_addr + i*VMEM_PAGE_SIZE; + mm->page_list[i].alloc_pages = 0; + mm->page_list[i].used = 0; + mm->page_list[i].first_pageno = -1; + } + + set_blocks_free(mm, 0, mm->num_pages); + + return 0; +} + +int +vmem_exit( + video_mm_t* mm +) +{ + if (mm == NULL) { + printk(KERN_INFO "vmem_exit: invalid handle\n"); + return -1; + } + + if (mm->free_tree) { + avltree_free(mm->free_tree); + } + if (mm->alloc_tree) { + avltree_free(mm->alloc_tree); + } + + VMEM_P_FREE(mm->page_list); + + mm->base_addr = 0; + mm->mem_size = 0; + mm->num_pages = 0; + mm->page_list = NULL; + mm->free_tree = NULL; + mm->alloc_tree = NULL; + mm->free_page_count = 0; + mm->alloc_page_count = 0; + return 0; +} + +unsigned long +vmem_alloc( + video_mm_t* mm, + int size, + unsigned long pid +) +{ + avl_node_t* node; + page_t* free_page; + int npages, free_size; + int alloc_pageno; + unsigned long ptr; + + if (mm == NULL) { + printk(KERN_INFO "vmem_alloc: invalid handle\n"); + return -1; + } + + if (size <= 0) return -1; + + npages = (size + VMEM_PAGE_SIZE -1)/VMEM_PAGE_SIZE; + + mm->free_tree = remove_approx_value(mm->free_tree, &node, MAKE_KEY(npages, 0)); + if (node == NULL) { + return -1; + } + free_page = node->page; + free_size = KEY_TO_VALUE(node->key); + + alloc_pageno = free_page->pageno; + set_blocks_alloc(mm, alloc_pageno, npages); + if (npages != free_size) { + int free_pageno = alloc_pageno + npages; + set_blocks_free(mm, free_pageno, (free_size-npages)); + } + + VMEM_P_FREE(node); + + ptr = mm->page_list[alloc_pageno].addr; + mm->alloc_page_count += npages; + mm->free_page_count -= npages; + + + return ptr; +} + +int +vmem_free( + video_mm_t* mm, + unsigned long ptr, + unsigned long pid +) +{ + unsigned long addr; + avl_node_t* found; + page_t* page; + int pageno, prev_free_pageno, next_free_pageno; + int prev_size, next_size; + int merge_page_no, merge_page_size, free_page_size; + + + if (mm == NULL) { + printk(KERN_INFO "vmem_free: invalid handle\n"); + return -1; + } + + addr = ptr; + + mm->alloc_tree = avltree_remove(mm->alloc_tree, &found, MAKE_KEY(addr, 0)); + if (found == NULL) { + printk(KERN_INFO "vmem_free: 0x%08x not found\n", (int)addr); + VMEM_ASSERT(0); + return -1; + } + + /* find previous free block */ + page = found->page; + pageno = page->pageno; + free_page_size = page->alloc_pages; + prev_free_pageno = pageno-1; + prev_size = -1; + if (prev_free_pageno >= 0) { + if (mm->page_list[prev_free_pageno].used == 0) { + prev_free_pageno = mm->page_list[prev_free_pageno].first_pageno; + prev_size = mm->page_list[prev_free_pageno].alloc_pages; + } + } + + /* find next free block */ + next_free_pageno = pageno + page->alloc_pages; + next_free_pageno = (next_free_pageno == mm->num_pages) ? -1 : next_free_pageno; + next_size = -1; + if (next_free_pageno >= 0) { + if (mm->page_list[next_free_pageno].used == 0) { + next_size = mm->page_list[next_free_pageno].alloc_pages; + } + } + VMEM_P_FREE(found); + + /* merge */ + merge_page_no = page->pageno; + merge_page_size = page->alloc_pages; + if (prev_size >= 0) { + mm->free_tree = avltree_remove(mm->free_tree, &found, MAKE_KEY(prev_size, prev_free_pageno)); + if (found == NULL) { + VMEM_ASSERT(0); + return -1; + } + merge_page_no = found->page->pageno; + merge_page_size += found->page->alloc_pages; + VMEM_P_FREE(found); + } + if (next_size >= 0) { + mm->free_tree = avltree_remove(mm->free_tree, &found, MAKE_KEY(next_size, next_free_pageno)); + if (found == NULL) { + VMEM_ASSERT(0); + return -1; + } + merge_page_size += found->page->alloc_pages; + VMEM_P_FREE(found); + } + + page->alloc_pages = 0; + page->first_pageno = -1; + + set_blocks_free(mm, merge_page_no, merge_page_size); + + mm->alloc_page_count -= free_page_size; + mm->free_page_count += free_page_size; + + return 0; +} + +int +vmem_get_info( + video_mm_t* mm, + vmem_info_t* info +) +{ + if (mm == NULL) { + printk(KERN_INFO "vmem_get_info: invalid handle\n"); + return -1; + } + + if (info == NULL) { + return -1; + } + + info->total_pages = mm->num_pages; + info->alloc_pages = mm->alloc_page_count; + info->free_pages = mm->free_page_count; + info->page_size = VMEM_PAGE_SIZE; + + return 0; +} + + + +#endif /* __CNM_VIDEO_MEMORY_MANAGEMENT_H__ */ \ No newline at end of file diff --git a/drivers/misc/sprd_coda7l/vpu.c b/drivers/misc/sprd_coda7l/vpu.c new file mode 100644 index 00000000..a1df00f0 --- /dev/null +++ b/drivers/misc/sprd_coda7l/vpu.c @@ -0,0 +1,1597 @@ +/* + * vpu.c + * + * linux device driver for VPU. + * + * Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library 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 Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "vpuconfig.h" +#include "vpu.h" +#include "TShark2_CODEC_AHB_Control_Register.h" + +#define LOG_TAG "CNM_VPU_DRV" + + +/* definitions to be changed as customer configuration */ +/* if you want to have clock gating scheme frame by frame */ +/* #define VPU_SUPPORT_CLOCK_CONTROL */ + +/* if the driver want to use interrupt service from kernel ISR */ +#define VPU_SUPPORT_ISR +#ifdef VPU_SUPPORT_ISR +/* if the driver want to disable and enable IRQ whenever interrupt asserted. */ +//#define VPU_IRQ_CONTROL +#endif + +/* if the platform driver knows the name of this driver */ +/* VPU_PLATFORM_DEVICE_NAME */ + +#define VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + +/* if this driver knows the dedicated video memory address */ +//#define VPU_SUPPORT_RESERVED_VIDEO_MEMORY + +#define VPU_PLATFORM_DEVICE_NAME "vdec" +#define VPU_CLK_NAME "vcodec" +#define VPU_DEV_NAME "vpu" + +#define __SPRD_MM_TIMEOUT (1000) + +/* if the platform driver knows this driver */ +/* the definition of VPU_REG_BASE_ADDR and VPU_REG_SIZE are not meaningful */ + +//Set register 0x3000_0110(phy_addr) +#define VPU_AXI_CLK_ADDR (0x62000004) +#define VPU_AXI_CLK_ENABLE 0x0A04060A +#define VPU_AXI_CLK_DISABLE 0x0A04040A + +#define VPU_REG_BASE_ADDR 0x62100000 +#define VPU_REG_SIZE (0x4000*MAX_NUM_VPU_CORE) + +#ifdef VPU_SUPPORT_ISR +#define VPU_IRQ_NUM (23+32) +#endif + +/* this definition is only for chipsnmedia FPGA board env */ +/* so for SOC env of customers can be ignored */ + + +#ifndef VM_RESERVED /*for kernel up to 3.7.0 version*/ +# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) +#endif + +#define VPU_MINOR MISC_DYNAMIC_MINOR + + +typedef struct vpu_drv_context_t { + struct fasync_struct *async_queue; + u32 open_count; /*!<< device reference count. Not instance count */ + + unsigned int freq_div; + + struct semaphore deint_mutex; + + struct clk *clk_coda7_axi; + struct clk *clk_coda7_cc; + struct clk *clk_coda7_apb; + + struct clk *clk_parent_axi; + struct clk *clk_parent_cc; + struct clk *clk_parent_apb; + + struct clk *clk_parent; + struct clk *clk_mm_i; + + unsigned int irq; + unsigned int version; + + //struct deint_fh *deint_fp; + struct device_node *dev_np; +} vpu_drv_context_t; + +/* To track the allocated memory buffer */ +typedef struct vpudrv_buffer_pool_t { + struct list_head list; + struct vpudrv_buffer_t vb; + struct file *filp; +} vpudrv_buffer_pool_t; + +/* To track the instance index and buffer in instance pool */ +typedef struct vpudrv_instanace_list_t { + struct list_head list; + unsigned long inst_idx; + unsigned long core_idx; + struct file *filp; +} vpudrv_instanace_list_t; + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + +#define VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE (62*1024*1024) +#define VPU_DRAM_PHYSICAL_BASE 0x86C00000 + +#include "vmm.h" +static video_mm_t s_vmem; +static vpudrv_buffer_t s_video_memory = {0}; +#else +#endif /*VPU_SUPPORT_RESERVED_VIDEO_MEMORY*/ + +typedef struct vpudrv_instance_pool_t { + unsigned char codecInstPool[MAX_NUM_INSTANCE][MAX_INST_HANDLE_SIZE]; + int vpu_instance_num; +} vpudrv_instance_pool_t; + +static int vpu_hw_reset(void); + +static void vpu_clk_disable(struct clk *clk); +static int vpu_clk_enable(struct clk *clk); +static struct clk *vpu_clk_get(struct device *dev); +static void vpu_clk_put(struct clk *clk); + + +/* end customer definition */ +static vpudrv_buffer_t s_instance_pool = {0}; +static vpudrv_buffer_t s_common_memory = {0}; +static vpu_drv_context_t s_vpu_drv_context; + +static struct clk *s_vpu_clk; + +static int s_vpu_open_ref_count; + +#ifdef VPU_SUPPORT_ISR +static int s_vpu_irq = VPU_IRQ_NUM; +#endif + +static unsigned long s_vpu_reg_phy_addr = 0; +static unsigned int s_vpu_power_status_mask = 0; + +static void __iomem *s_vpu_reg_virt_addr = NULL; +static void __iomem *s_vpu_power_reg_vir_addr = NULL; +static void __iomem *s_vpu_power_status_vir_addr = NULL; + +static int s_interrupt_flag; +static wait_queue_head_t s_interrupt_wait_q; + +static spinlock_t s_vpu_lock = __SPIN_LOCK_UNLOCKED(s_vpu_lock); +static DEFINE_SEMAPHORE(s_vpu_sem); +static struct list_head s_vbp_head = LIST_HEAD_INIT(s_vbp_head); +static struct list_head s_inst_list_head = LIST_HEAD_INIT(s_inst_list_head); + + +static vpu_bit_firmware_info_t s_bit_firmware_info[MAX_NUM_VPU_CORE]; +static struct vpu_dev vpu_hw_dev; + + +#define BIT_BASE 0x0000 +#define BIT_CODE_RUN (BIT_BASE + 0x000) +#define BIT_CODE_DOWN (BIT_BASE + 0x004) +#define BIT_INT_CLEAR (BIT_BASE + 0x00C) +#define BIT_INT_STS (BIT_BASE + 0x010) +#define BIT_CODE_RESET (BIT_BASE + 0x014) +#define BIT_BUSY_FLAG (BIT_BASE + 0x160) +#define BIT_RUN_COMMAND (BIT_BASE + 0x164) +#define BIT_RUN_INDEX (BIT_BASE + 0x168) +#define BIT_RUN_COD_STD (BIT_BASE + 0x16C) +#define BIT_CUR_PC (BIT_BASE + 0x018) +#define BIT_INT_REASON (BIT_BASE + 0x174) +#define VPU_PRODUCT_CODE_REGISTER (BIT_BASE + 0x1044) + +#ifdef CONFIG_PM +/* implement to power management functions */ +static u32 s_vpu_reg_store[MAX_NUM_VPU_CORE][64]; +static u32 s_run_index; +static u32 s_run_codstd; +#endif + +static int vpu_resume(struct platform_device *pdev); +static int vpu_suspend(struct platform_device *pdev, pm_message_t state); +static int vpu_set_mm_clk(void); +static int vpu_set_clk_by_register(void); +static int vpu_clk_free(vpu_drv_context_t* vpu_context); +static int vpu_power_on(); +static int vpu_power_shutdown(); + +#define ReadVpuRegister(addr) *(volatile unsigned int *)(s_vpu_reg_virt_addr + s_bit_firmware_info[core].reg_base_offset + addr) +#define WriteVpuRegister(addr, val) *(volatile unsigned int *)(s_vpu_reg_virt_addr + s_bit_firmware_info[core].reg_base_offset + addr) = (unsigned int)val +#define WriteVpu(addr, val) *(volatile unsigned int *)(addr) = (unsigned int)val + +#define DEFAULT_FREQ_DIV 0x0 + +struct clock_name_map_t { + unsigned long freq; + char *name; +}; + +static struct clock_name_map_t clock_coda7l_axi_map[] = { + {192000000,"clk_192m"}, + {153600000,"clk_153m6"}, + {128000000,"clk_128m"}, + {76800000,"clk_76m8"} +}; + +static struct clock_name_map_t clock_coda7l_cc_map[] = { + {192000000,"clk_192m"}, + {153600000,"clk_153m6"}, + {128000000,"clk_128m"}, + {76800000,"clk_76m8"} +}; + +static struct clock_name_map_t clock_coda7l_apb_map[] = { + {128000000,"clk_128m"}, + {96000000,"clk_96m"}, + {76800000,"clk_76m8"}, + {26000000,"ext_26m"} +}; + +static int max_freq_level = ARRAY_SIZE(clock_coda7l_axi_map); + +static char *vpu_get_clk_src_name(unsigned int freq_level, struct clock_name_map_t clk_map[]) +{ + if (freq_level >= max_freq_level ) { + printk(KERN_INFO "set freq_level to 0"); + freq_level = 0; + } + + return clk_map[freq_level].name; +} + +static int find_vpu_freq_level(unsigned long freq, struct clock_name_map_t clk_map[]) +{ + int level = 0; + int i; + for (i = 0; i < max_freq_level; i++) { + if (clk_map[i].freq == freq) { + level = i; + break; + } + } + return level; +} + +static int vpu_power_on() +{ + unsigned int power_state1, power_state2, power_state3; + unsigned long timeout = jiffies + msecs_to_jiffies(__SPRD_MM_TIMEOUT); + unsigned int count = 0; + + __raw_writel(BIT(24) | __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + __raw_writel((~BIT(25)) & __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + + do { + power_state1 = (__raw_readl(s_vpu_power_status_vir_addr) & s_vpu_power_status_mask); + power_state2 = (__raw_readl(s_vpu_power_status_vir_addr) & s_vpu_power_status_mask); + power_state3 = (__raw_readl(s_vpu_power_status_vir_addr) & s_vpu_power_status_mask); + count++; + + if(time_after(jiffies, timeout)) { + return -1; + } + } while(power_state1 != power_state2 || power_state2 != power_state3 || power_state1 != 0); + + return 0; +} + +static int vpu_power_shutdown() +{ + __raw_writel(BIT(25) | __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + __raw_writel((~BIT(24)) & __raw_readl(s_vpu_power_reg_vir_addr), s_vpu_power_reg_vir_addr); + + return 0; +} + +static int vpu_alloc_dma_buffer(vpudrv_buffer_t *vb) +{ + if (!vb) + return -1; + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + vb->phys_addr = (unsigned long)vmem_alloc(&s_vmem, vb->size, 0); + if ((unsigned long)vb->phys_addr == (unsigned long)-1) { + vpu_loge("reserved Physical memory allocation error size=%d, base_addr=0x%x, mem_size=%d\n", vb->size, (int)s_vmem.base_addr, (int)s_vmem.mem_size); + return -1; + } + + vb->base = (unsigned long)(s_video_memory.base + (vb->phys_addr - s_video_memory.phys_addr)); +#else + vb->base = (unsigned long)dma_alloc_coherent(NULL, PAGE_ALIGN(vb->size), (dma_addr_t *) (&vb->phys_addr), GFP_DMA | GFP_KERNEL); + if ((void *)(vb->base) == NULL) { + vpu_loge("dynamic Physical memory allocation error size=%d\n", vb->size); + return -1; + } +#endif + + + return 0; +} + +static void vpu_free_dma_buffer(vpudrv_buffer_t *vb) +{ + if (!vb) + return; + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (vb->base) + vmem_free(&s_vmem, vb->phys_addr, 0); +#else + if (vb->base) + dma_free_coherent(0, PAGE_ALIGN(vb->size), (void *)vb->base, vb->phys_addr); +#endif + +} + +static int vpu_free_instances(struct file *filp) +{ + vpudrv_instanace_list_t *vil, *n; + vpudrv_instance_pool_t *vip; + unsigned char *vip_base; + int instance_pool_size_per_core; + unsigned char *vdi_mutexes_base; + const int PTHREAD_MUTEX_T_DESTROY_VALUE = 0xdead10cc; + + vpu_logd("vpu_free_instances inter. sizeof(vpudrv_instance_pool_t)=%d \n", sizeof(vpudrv_instance_pool_t)); + + instance_pool_size_per_core = (s_instance_pool.size/MAX_NUM_VPU_CORE); /* s_instance_pool.size assigned to the size of all core once call VDI_IOCTL_GET_INSTANCE_POOL by user. */ + + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->filp == filp) { + s_vpu_open_ref_count--; + vip_base = (unsigned char *)(s_instance_pool.base + (instance_pool_size_per_core*vil->core_idx)); + vpu_logd("vpu_free_instances detect instance crash\n"); + vpu_logd("instIdx=%d, coreIdx=%d, vip_base=%p, instance_pool_size_per_core=%d\n", (int)vil->inst_idx, (int)vil->core_idx, vip_base, (int)instance_pool_size_per_core); + vip = (vpudrv_instance_pool_t *)vip_base; + if (vip) { + memset(&vip->codecInstPool[vil->inst_idx], 0x00, 4); /* only first 4 byte is key point to free the corresponding instance. */ + vip->vpu_instance_num = s_vpu_open_ref_count; +#define PTHREAD_MUTEX_T_HANDLE_SIZE 4 + vdi_mutexes_base = (unsigned char *)(vip_base + (instance_pool_size_per_core - PTHREAD_MUTEX_T_HANDLE_SIZE*4)); + vpu_logd("vpu_free_instances : force to destroy vdi_mutexes_base=%p in userspace, vip->vpu_instance_num=%d\n", + vdi_mutexes_base, vip->vpu_instance_num); + if (vdi_mutexes_base) { + int i; + for (i=0; i < 4; i++) { + memcpy(vdi_mutexes_base, &PTHREAD_MUTEX_T_DESTROY_VALUE, PTHREAD_MUTEX_T_HANDLE_SIZE); + vdi_mutexes_base += PTHREAD_MUTEX_T_HANDLE_SIZE; + } + } + } + list_del(&vil->list); + kfree(vil); + } + } + + return 1; +} + +static int vpu_free_buffers(struct file *filp) +{ + vpudrv_buffer_pool_t *pool, *n; + vpudrv_buffer_t vb; + + vpu_logd("vpu_free_buffers\n"); + + list_for_each_entry_safe(pool, n, &s_vbp_head, list) + { + if (pool->filp == filp) { + vb = pool->vb; + if (vb.base) { + vpu_free_dma_buffer(&vb); + list_del(&pool->list); + kfree(pool); + } + } + + } + + return 0; +} + +static irqreturn_t vpu_irq_handler(int irq, void *dev_id) +{ + vpu_drv_context_t *dev = (vpu_drv_context_t *)dev_id; + + /* this can be removed. it also work in VPU_WaitInterrupt of API function */ + int core; + + if (s_vpu_drv_context.open_count <= 0) + { + //printk(KERN_ERR "This interrupt signal is not for VPU\n"); + return IRQ_NONE; + } + +#ifdef VPU_IRQ_CONTROL + disable_irq_nosync(s_vpu_irq); +#endif + + for (core = 0; core < MAX_NUM_VPU_CORE; core++) { + /*it means that we didn't get an information the current core from API layer. No core activated.*/ + if (s_bit_firmware_info[core].size == 0) + continue; + + if (ReadVpuRegister(BIT_INT_STS)) + { + WriteVpuRegister(BIT_INT_CLEAR, 0x1); + } + else + { + return IRQ_NONE; + } + } + + if (dev->async_queue) + kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* notify the interrupt to user space */ + + s_interrupt_flag = 1; + + wake_up_interruptible(&s_interrupt_wait_q); + return IRQ_HANDLED; +} + +static int vpu_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + if(vpu_power_on()) { + return -EINVAL; + } + + ret = vpu_set_mm_clk(); + +#if defined(CONFIG_SPRD_IOMMU) + sprd_iommu_module_enable(IOMMU_MM); +#endif + + spin_lock(&s_vpu_lock); + s_vpu_drv_context.open_count++; + filp->private_data = (void *)(&s_vpu_drv_context); + spin_unlock(&s_vpu_lock); + + vpu_logi("[VPUDRV] vpu_open done, open_count = %d\n", s_vpu_drv_context.open_count); + + return ret; +} + +/*static int vpu_ioctl(struct inode *inode, struct file *filp, u_int cmd, u_long arg) // for kernel 2.6.9 of C&M*/ +static long vpu_ioctl(struct file *filp, u_int cmd, u_long arg) +{ + int ret = 0; + switch (cmd) { + case VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY: + { + vpudrv_buffer_pool_t *vbp; + + down(&s_vpu_sem); + + vbp = kzalloc(sizeof(*vbp), GFP_KERNEL); + if (!vbp) { + up(&s_vpu_sem); + return -ENOMEM; + } + + ret = copy_from_user(&(vbp->vb), (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + up(&s_vpu_sem); + return -EFAULT; + } + + ret = vpu_alloc_dma_buffer(&(vbp->vb)); + if (ret == -1) { + ret = -ENOMEM; + kfree(vbp); + up(&s_vpu_sem); + break; + } + ret = copy_to_user((void __user *)arg, &(vbp->vb), sizeof(vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + ret = -EFAULT; + up(&s_vpu_sem); + break; + } + + vbp->filp = filp; + spin_lock(&s_vpu_lock); + list_add(&vbp->list, &s_vbp_head); + spin_unlock(&s_vpu_lock); + + up(&s_vpu_sem); + } + break; + case VDI_IOCTL_FREE_PHYSICALMEMORY: + { + vpudrv_buffer_pool_t *vbp, *n; + vpudrv_buffer_t vb; + + down(&s_vpu_sem); + + ret = copy_from_user(&vb, (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret) { + up(&s_vpu_sem); + return -EACCES; + } + + if (vb.base) + vpu_free_dma_buffer(&vb); + + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vbp, n, &s_vbp_head, list) + { + if (vbp->vb.base == vb.base) { + list_del(&vbp->list); + kfree(vbp); + break; + } + } + spin_unlock(&s_vpu_lock); + + up(&s_vpu_sem); + + } + break; + case VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO: + { +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (s_video_memory.base != 0) { + ret = copy_to_user((void __user *)arg, &s_video_memory, sizeof(vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = -EFAULT; + } +#endif + } + break; + + case VDI_IOCTL_WAIT_INTERRUPT: + { + u32 timeout = (u32) arg; + + ret = wait_event_interruptible_timeout(s_interrupt_wait_q, s_interrupt_flag != 0, msecs_to_jiffies(timeout)); + if (ret == 0) { + ret = -ETIME; + break; + } + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + ret = 0; + s_interrupt_flag = 0; +#ifdef VPU_IRQ_CONTROL + enable_irq(s_vpu_irq); +#endif + } + break; + + case VDI_IOCTL_SET_CLOCK_GATE: + { + u32 clkgate; + + if (get_user(clkgate, (u32 __user *) arg)) + return -EFAULT; +#ifdef VPU_SUPPORT_CLOCK_CONTROL + if (clkgate) + vpu_clk_enable(s_vpu_clk); + else + vpu_clk_disable(s_vpu_clk); +#endif + + } + break; + case VDI_IOCTL_GET_INSTANCE_POOL: + { + down(&s_vpu_sem); + + vpu_logi("[VPUDRV] VDI_IOCTL_GET_INSTANCE_POOL\n"); + + if (s_instance_pool.base != 0) { + ret = copy_to_user((void __user *)arg, &s_instance_pool, sizeof(vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = copy_from_user(&s_instance_pool, (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + if (vpu_alloc_dma_buffer(&s_instance_pool) != -1) + { + vpu_logi("[VPUDRV] vpu_alloc_dma_buffer sucessfully\n"); + memset((void *)s_instance_pool.base, 0x0, s_instance_pool.size); /*clearing memory*/ + ret = copy_to_user((void __user *)arg, &s_instance_pool, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + /* success to get memory for instance pool */ + up(&s_vpu_sem); + break; + } + } + + } + ret = -EFAULT; + } + + up(&s_vpu_sem); + } + break; + case VDI_IOCTL_GET_COMMON_MEMORY: + { + if (s_common_memory.base != 0) { + ret = copy_to_user((void __user *)arg, &s_common_memory, sizeof(vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = copy_from_user(&s_common_memory, (vpudrv_buffer_t *)arg, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + if (vpu_alloc_dma_buffer(&s_common_memory) != -1) { + ret = copy_to_user((void __user *)arg, &s_common_memory, sizeof(vpudrv_buffer_t)); + if (ret == 0) { + /* success to get memory for common memory */ + break; + } + } + } + + ret = -EFAULT; + } + } + break; + case VDI_IOCTL_OPEN_INSTANCE: + { + vpudrv_inst_info_t inst_info; + vpudrv_instanace_list_t *vil, *n; + + vil = kzalloc(sizeof(*vil), GFP_KERNEL); + if (!vil) + return -ENOMEM; + + if (copy_from_user(&inst_info, (vpudrv_inst_info_t *)arg, sizeof(vpudrv_inst_info_t))) { + kfree(vil); + return -EFAULT; + } + + vil->inst_idx = inst_info.inst_idx; + vil->core_idx = inst_info.core_idx; + vil->filp = filp; + spin_lock(&s_vpu_lock); + list_add(&vil->list, &s_inst_list_head); + + inst_info.inst_open_count = 0; /* counting the current open instance number */ + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + s_vpu_open_ref_count++; /* flag just for that vpu is in opened or closed */ + + if (copy_to_user((void __user *)arg, &inst_info, sizeof(vpudrv_inst_info_t))) { + kfree(vil); + return -EFAULT; + } + + vpu_logd("[VPUDRV] VDI_IOCTL_OPEN_INSTANCE core_idx=%d, inst_idx=%d, s_vpu_open_ref_count=%d, inst_open_count=%d\n", + (int)inst_info.core_idx, (int)inst_info.inst_idx, s_vpu_open_ref_count, inst_info.inst_open_count); + } + break; + case VDI_IOCTL_CLOSE_INSTANCE: + { + vpudrv_inst_info_t inst_info; + vpudrv_instanace_list_t *vil, *n; + + if (copy_from_user(&inst_info, (vpudrv_inst_info_t *)arg, sizeof(vpudrv_inst_info_t))) + return -EFAULT; + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->inst_idx == inst_info.inst_idx && vil->core_idx == inst_info.core_idx) { + list_del(&vil->list); + kfree(vil); + break; + } + } + + inst_info.inst_open_count = 0; /* counting the current open instance number */ + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + s_vpu_open_ref_count--; /* flag just for that vpu is in opened or closed */ + + if (copy_to_user((void __user *)arg, &inst_info, sizeof(vpudrv_inst_info_t))) + return -EFAULT; + + vpu_logd("[VPUDRV] VDI_IOCTL_CLOSE_INSTANCE core_idx=%d, inst_idx=%d, s_vpu_open_ref_count=%d, inst_open_count=%d\n", + (int)inst_info.core_idx, (int)inst_info.inst_idx, s_vpu_open_ref_count, inst_info.inst_open_count); + } + break; + case VDI_IOCTL_GET_INSTANCE_NUM: + { + vpudrv_inst_info_t inst_info; + vpudrv_instanace_list_t *vil, *n; + + ret = copy_from_user(&inst_info, (vpudrv_inst_info_t *)arg, sizeof(vpudrv_inst_info_t)); + if (ret != 0) + break; + + inst_info.inst_open_count = 0; + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + ret = copy_to_user((void __user *)arg, &inst_info, sizeof(vpudrv_inst_info_t)); + + vpu_logd("[VPUDRV] VDI_IOCTL_GET_INSTANCE_NUM core_idx=%d, inst_idx=%d, open_count=%d\n", (int)inst_info.core_idx, (int)inst_info.inst_idx, inst_info.inst_open_count); + + } + break; + case VDI_IOCTL_RESET: + { + vpu_hw_reset(); + } + break; + default: + { + printk(KERN_ERR "[VPUDRV] No such IOCTL, cmd is %d\n", cmd); + } + break; + } + return ret; +} + + +static ssize_t vpu_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) +{ + + return -1; +} + +static ssize_t vpu_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) +{ + if (!buf) { + vpu_loge("vpu_write buf = NULL error \n"); + return -EFAULT; + } + + if (len == sizeof(vpu_bit_firmware_info_t)) { + vpu_bit_firmware_info_t *bit_firmware_info; + + bit_firmware_info = kzalloc(sizeof(vpu_bit_firmware_info_t), GFP_KERNEL); + if (!bit_firmware_info) { + vpu_loge("vpu_write bit_firmware_info allocation error \n"); + return -EFAULT; + } + + if (copy_from_user(bit_firmware_info, buf, len)) { + vpu_loge("vpu_write copy_from_user error for bit_firmware_info\n"); + kfree(bit_firmware_info); + return -EFAULT; + } + + if (bit_firmware_info->size == sizeof(vpu_bit_firmware_info_t)) { + vpu_logd("vpu_write set bit_firmware_info coreIdx=0x%x, reg_base_offset=0x%x size=0x%x, bit_code[0]=0x%x\n", + bit_firmware_info->core_idx, (int)bit_firmware_info->reg_base_offset, bit_firmware_info->size, bit_firmware_info->bit_code[0]); + + if (bit_firmware_info->core_idx > MAX_NUM_VPU_CORE) { + vpu_loge("vpu_write coreIdx[%d] is exceeded than MAX_NUM_VPU_CORE[%d]\n", bit_firmware_info->core_idx, MAX_NUM_VPU_CORE); + kfree(bit_firmware_info); + return -ENODEV; + } + + memcpy((void *)&s_bit_firmware_info[bit_firmware_info->core_idx], bit_firmware_info, sizeof(vpu_bit_firmware_info_t)); + kfree(bit_firmware_info); + return len; + } + + kfree(bit_firmware_info); + } + + + + + return -1; +} + +static int vpu_release(struct inode *inode, struct file *filp) +{ + int reg_addr; + + spin_lock(&s_vpu_lock); + + vpu_logi("[VPUDRV] vpu_release, open_count= %d\n", s_vpu_drv_context.open_count); + + /* found and free the not handled buffer by user applications */ + vpu_free_buffers(filp); + + /* found and free the not closed instance by user applications */ + vpu_free_instances(filp); + s_vpu_drv_context.open_count--; + if (s_vpu_drv_context.open_count == 0) { + if (s_instance_pool.base) { + vpu_logi("[VPUDRV] free instance pool\n"); + vpu_free_dma_buffer(&s_instance_pool); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_logi("[VPUDRV] free common memory\n"); + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + } + spin_unlock(&s_vpu_lock); + + vpu_clk_free(&s_vpu_drv_context); + + if(s_vpu_drv_context.open_count == 0) { + vpu_power_shutdown(); + } + +#if defined(CONFIG_SPRD_IOMMU) + sprd_iommu_module_disable(IOMMU_MM); +#endif + + return 0; +} + + +static int vpu_fasync(int fd, struct file *filp, int mode) +{ + struct vpu_drv_context_t *dev = (struct vpu_drv_context_t *)filp->private_data; + return fasync_helper(fd, filp, mode, &dev->async_queue); +} + + +static int vpu_map_to_register(struct file *fp, struct vm_area_struct *vm) +{ + unsigned long pfn; + + vm->vm_flags |= VM_IO | VM_RESERVED; + vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot); + pfn = s_vpu_reg_phy_addr >> PAGE_SHIFT; + + return remap_pfn_range(vm, vm->vm_start, pfn, vm->vm_end-vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0; +} + +static int vpu_map_to_physical_memory(struct file *fp, struct vm_area_struct *vm) +{ + vm->vm_flags |= VM_IO | VM_RESERVED; + vm->vm_page_prot = pgprot_writecombine(vm->vm_page_prot); + + return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, vm->vm_end-vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0; +} +static int vpu_map_to_instance_pool_memory(struct file *fp, struct vm_area_struct *vm) +{ + return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, vm->vm_end-vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0; +} + +/*! + * @brief memory map interface for vpu file operation + * @return 0 on success or negative error code on error + */ +static int vpu_mmap(struct file *fp, struct vm_area_struct *vm) +{ + printk("[%s], vm_pgoff = %ld \n", __FUNCTION__, vm->vm_pgoff); + + if (vm->vm_pgoff) { + if (vm->vm_pgoff == (s_instance_pool.phys_addr>>PAGE_SHIFT)) + return vpu_map_to_instance_pool_memory(fp, vm); + + return vpu_map_to_physical_memory(fp, vm); + } else { + return vpu_map_to_register(fp, vm); + } +} + +struct file_operations vpu_fops = { + .owner = THIS_MODULE, + .open = vpu_open, + .read = vpu_read, + .write = vpu_write, + .unlocked_ioctl = vpu_ioctl, + .release = vpu_release, + .fasync = vpu_fasync, + .mmap = vpu_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl = vpu_ioctl, +#endif +}; + +static struct miscdevice vpu_dev = { + .minor = VPU_MINOR, + .name = "sprd_coda7l", + .fops = &vpu_fops, +}; + + +#ifdef CONFIG_OF +static const struct of_device_id of_match_table_coda7l[] = { + { .compatible = "sprd,sprd_coda7l", }, + { }, +}; + +static int vpu_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct resource res; + u32 clock_parent_info[2]; + u32 power_regs_info[4]; + int i, ret; + + ret = of_address_to_resource(np, 0, &res); + if(ret < 0) { + dev_err(dev, "no reg of property specified\n"); + printk(KERN_ERR "vpu: failed to parse_dt!\n"); + return -EINVAL; + } + + s_vpu_reg_phy_addr = res.start; + s_vpu_reg_virt_addr = ioremap_nocache(res.start, resource_size(&res)); + if(!s_vpu_reg_virt_addr) + BUG(); + + s_vpu_drv_context.irq = irq_of_parse_and_map(np, 0); + s_vpu_drv_context.dev_np = np; + + printk(KERN_INFO "vpu_parse_dt , irq = %d, SPRD_VPP_PHYS = %p, SPRD_VPP_BASE = %p\n", + s_vpu_drv_context.irq, (void*)s_vpu_reg_phy_addr, (void*)s_vpu_reg_virt_addr); + + ret = of_property_read_u32_array(np, "clock-parent-info", clock_parent_info, 2); + if(0 != ret) { + printk(KERN_ERR "vpu: read clock-parent-info fail (%d)\n", ret); + return -EINVAL; + } + + max_freq_level = clock_parent_info[1]; + if (max_freq_level > 4) { + printk(KERN_ERR "vpu: max_freq_level is invalid\n"); + return -EINVAL; + } + + for (i = 0; i < max_freq_level; i++) { + struct clk *clk_parent; + char *name_parent; + unsigned long frequency; + + name_parent = of_clk_get_parent_name(np, i+clock_parent_info[0]); + clk_parent = clk_get(NULL, name_parent); + frequency = clk_get_rate(clk_parent); + + clock_coda7l_axi_map[i].name = name_parent; + clock_coda7l_axi_map[i].freq = frequency; + + clock_coda7l_cc_map[i].name = name_parent; + clock_coda7l_cc_map[i].freq = frequency; + + name_parent = of_clk_get_parent_name(np, i+clock_parent_info[0]+clock_parent_info[1]); + clk_parent = clk_get(NULL, name_parent); + frequency = clk_get_rate(clk_parent); + + clock_coda7l_apb_map[i].name = name_parent; + clock_coda7l_apb_map[i].freq = frequency; + } + + ret = of_property_read_u32_array(np, "power-regs-info", power_regs_info, 4); + if(0 != ret) { + printk(KERN_ERR "vpu: read power_regs_info fail (%d)\n", ret); + return -EINVAL; + } + + s_vpu_power_status_mask = power_regs_info[3]; + s_vpu_power_reg_vir_addr = ioremap_nocache(power_regs_info[0], 4); + s_vpu_power_status_vir_addr = ioremap_nocache(power_regs_info[2], 4); + if(!(s_vpu_power_reg_vir_addr && s_vpu_power_status_vir_addr)) { + printk(KERN_ERR "vpu power regs remap errorl \n"); + return -EINVAL; + } + + return 0; +} +#else +static int vpu_parse_dt( + struct device *dev) +{ + //vsp_hw_dev.irq = IRQ_VSP_INT; + return 0; +} +#endif + + +static int vpu_probe(struct platform_device *pdev) +{ + int err = 0; + int ret; + int reg_addr; + struct resource *res = NULL; + + vpu_logi("[VPUDRV] vpu_probe\n"); + +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + ret = vpu_parse_dt(&pdev->dev); + } +#else + ret = vpu_parse_dt(&pdev->dev); +#endif + + s_vpu_drv_context.freq_div = DEFAULT_FREQ_DIV; + s_vpu_drv_context.clk_mm_i= NULL; + s_vpu_drv_context.clk_parent_axi= NULL; + s_vpu_drv_context.clk_parent_cc= NULL; + s_vpu_drv_context.clk_parent_apb= NULL; + s_vpu_drv_context.clk_coda7_apb= NULL; + s_vpu_drv_context.clk_coda7_axi= NULL; + s_vpu_drv_context.clk_coda7_cc= NULL; + + ret = misc_register(&vpu_dev); + if (ret) { + printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n", + VPU_MINOR, ret); + goto ERROR_PROVE_DEVICE; + } + +#ifdef VPU_SUPPORT_ISR + /*if (pdev) + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) {// if platform driver is implemented + s_vpu_irq = res->start; + vpu_logi("[VPUDRV] : vpu irq number get from platform driver irq=0x%x\n", s_vpu_irq); + } else { + vpu_logi("[VPUDRV] : vpu irq number get from defined value irq=0x%x\n", s_vpu_irq); + }*/ + s_vpu_irq = s_vpu_drv_context.irq; + + err = request_irq(s_vpu_irq, vpu_irq_handler, IRQF_SHARED, "VPU_CODEC_IRQ", (void *)(&s_vpu_drv_context)); + if (err) { + printk(KERN_ERR "[VPUDRV] : fail to register interrupt handler\n"); + goto ERROR_PROVE_DEVICE; + } +#endif + + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + s_video_memory.size = VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE; + s_video_memory.phys_addr = VPU_DRAM_PHYSICAL_BASE; + s_video_memory.base = (unsigned long)ioremap(s_video_memory.phys_addr, PAGE_ALIGN(s_video_memory.size)); + if (!s_video_memory.base) { + printk(KERN_ERR "[VPUDRV] : fail to remap video memory physical phys_addr=0x%x, base=0x%x, size=%d\n", + (int)s_video_memory.phys_addr, (int)s_video_memory.base, (int)s_video_memory.size); + goto ERROR_PROVE_DEVICE; + } + + if (vmem_init(&s_vmem, s_video_memory.phys_addr, s_video_memory.size) < 0) { + printk(KERN_ERR "[VPUDRV] : fail to init vmem system\n"); + goto ERROR_PROVE_DEVICE; + } + vpu_logi("[VPUDRV] success to probe vpu device with reserved video memory phys_addr=0x%x, base = 0x%x\n", + (int) s_video_memory.phys_addr, (int)s_video_memory.base); +#else + vpu_logi("[VPUDRV] success to probe vpu device with non reserved video memory\n"); +#endif + + return 0; + + +ERROR_PROVE_DEVICE: + + misc_deregister(&vpu_dev); + + if (s_vpu_reg_virt_addr) + iounmap(s_vpu_reg_virt_addr); + + return err; +} + +static int vpu_remove(struct platform_device *pdev) +{ + vpu_logd("vpu_remove\n"); +#ifdef VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + + misc_deregister(&vpu_dev); + + if (s_instance_pool.base) { + vpu_free_dma_buffer(&s_instance_pool); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (s_video_memory.base) { + iounmap((void *)s_video_memory.base); + s_video_memory.base = 0; + + vmem_exit(&s_vmem); + } +#endif + +#ifdef VPU_SUPPORT_ISR + if (s_vpu_drv_context.irq) + free_irq(s_vpu_drv_context.irq, &s_vpu_drv_context); +#endif + + vpu_clk_put(s_vpu_clk); + + + +#endif /*VPU_SUPPORT_PLATFORM_DRIVER_REGISTER*/ + + + return 0; +} + +#ifdef CONFIG_PM +static int vpu_suspend(struct platform_device *pdev, pm_message_t state) +{ + vpu_logd("vpu_suspend\n"); + return 0; +} +static int vpu_resume(struct platform_device *pdev) +{ + vpu_logd("vpu_resume\n"); + return 0; +} +#else +#define vpu_suspend NULL +#define vpu_resume NULL +#endif /* !CONFIG_PM */ + + +static struct platform_driver vpu_driver = { + .probe = vpu_probe, + .remove = vpu_remove, + .suspend = vpu_suspend, + .resume = vpu_resume, + .driver = { + .owner = THIS_MODULE, + .name = "sprd_coda7l", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(of_match_table_coda7l) , +#endif + }, +}; + +static int __init vpu_init(void) +{ + int res = 0; + + vpu_logd("vpu_init, REG_AON_APB_BOND_OPT0 = 0x%x\n", __raw_readl(REG_AON_APB_BOND_OPT0)); + + if(__raw_readl(REG_AON_APB_BOND_OPT0) & (1<<12)) { + return 0; + } + + init_waitqueue_head(&s_interrupt_wait_q); + s_common_memory.base = 0; + s_instance_pool.base = 0; +#ifdef VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + res = platform_driver_register(&vpu_driver); +#else + res = platform_driver_register(&vpu_driver); + res = vpu_probe(NULL); +#endif + vpu_power_shutdown(); + vpu_logd("end vpu_init result=0x%x\n", res); + + return res; +} + +static void __exit vpu_exit(void) +{ +#ifdef VPU_SUPPORT_PLATFORM_DRIVER_REGISTER + vpu_logd("vpu_exit\n"); + + if(__raw_readl(REG_AON_APB_BOND_OPT0) & (1<<12)) { + return ; + } + + platform_driver_unregister(&vpu_driver); + +#else + + vpu_clk_put(s_vpu_clk); + + if (s_instance_pool.base) { + vpu_free_dma_buffer(&s_instance_pool); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + +#ifdef VPU_SUPPORT_RESERVED_VIDEO_MEMORY + if (s_video_memory.base) { + iounmap((void *)s_video_memory.base); + s_video_memory.base = 0; + + vmem_exit(&s_vmem); + } +#endif + + if (s_vpu_major > 0) { + cdev_del(&s_vpu_cdev); + unregister_chrdev_region(s_vpu_major, 1); + s_vpu_major = 0; + } + +#ifdef VPU_SUPPORT_ISR + if (s_vpu_irq) + free_irq(s_vpu_irq, &s_vpu_drv_context); +#endif + +#endif + + return; +} + + + +MODULE_AUTHOR("A customer using C&M VPU, Inc."); +MODULE_DESCRIPTION("VPU linux driver"); +MODULE_LICENSE("GPL"); + +module_init(vpu_init); +module_exit(vpu_exit); + +int vpu_hw_reset(void) +{ + vpu_logd("request vpu reset from application. \n"); + return 0; +} + +static int vpu_clk_free(vpu_drv_context_t* vpu_context) +{ + if (vpu_context->clk_coda7_apb) { + clk_disable(vpu_context->clk_coda7_apb); + } + + if (vpu_context->clk_coda7_axi) { + clk_disable(vpu_context->clk_coda7_axi); + } + + if (vpu_context->clk_coda7_cc) { + clk_disable(vpu_context->clk_coda7_cc); + } + + if (vpu_context->clk_coda7_apb) { + clk_unprepare(vpu_context->clk_coda7_apb); + } + + if (vpu_context->clk_coda7_axi) { + clk_unprepare(vpu_context->clk_coda7_axi); + } + + if (vpu_context->clk_coda7_cc) { + clk_unprepare(vpu_context->clk_coda7_cc); + } + + if (vpu_context->clk_mm_i) { + clk_disable_unprepare(vpu_context->clk_mm_i); + } + + return 0; +} + +static int vpu_set_parent_for_coda7l_clk() +{ + struct clk *clk_parent; + char *name_parent; + int ret =0; + + name_parent = vpu_get_clk_src_name(3, clock_coda7l_axi_map); + clk_parent = clk_get(NULL, name_parent); + printk(KERN_ERR "clock[%s]: get parent in probe[%s] by clk_get()!\n", "clk_coda7_axi", name_parent); + if ((!clk_parent )|| IS_ERR(clk_parent) ) { + printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_coda7_axi", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_axi= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_axi, s_vpu_drv_context.clk_parent_axi); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_axi"); + ret = -EINVAL; + } + + name_parent = vpu_get_clk_src_name(s_vpu_drv_context.freq_div, clock_coda7l_axi_map); + clk_parent = clk_get(NULL, name_parent); + printk(KERN_ERR "clock[%s]: get parent in probe[%s] by clk_get()!\n", "clk_coda7_axi", name_parent); + if ((!clk_parent )|| IS_ERR(clk_parent) ) { + printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_coda7_axi", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_axi= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_axi, s_vpu_drv_context.clk_parent_axi); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_axi"); + ret = -EINVAL; + } + + printk(KERN_ERR "vpu parent clock name %s, freq: %dHz\n", name_parent, (int)clk_get_rate(s_vpu_drv_context.clk_coda7_axi)); + + name_parent = vpu_get_clk_src_name(3, clock_coda7l_cc_map); + clk_parent = clk_get(NULL, name_parent); + if ((!clk_parent )|| IS_ERR(clk_parent) ) { + printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_coda7_cc", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_cc= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_cc, s_vpu_drv_context.clk_parent_cc); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_cc"); + ret = -EINVAL; + } + + name_parent = vpu_get_clk_src_name(s_vpu_drv_context.freq_div, clock_coda7l_cc_map); + clk_parent = clk_get(NULL, name_parent); + if ((!clk_parent )|| IS_ERR(clk_parent) ) { + printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_coda7_cc", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_cc= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_cc, s_vpu_drv_context.clk_parent_cc); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_cc"); + ret = -EINVAL; + } + + printk(KERN_ERR "vpu parent clock name %s, freq: %dHz\n", name_parent, (int)clk_get_rate(s_vpu_drv_context.clk_coda7_cc)); + + name_parent = vpu_get_clk_src_name(3, clock_coda7l_apb_map); + clk_parent = clk_get(NULL, name_parent); + if ((!clk_parent )|| IS_ERR(clk_parent) ) { + printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_coda7_apb", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_apb= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_apb, s_vpu_drv_context.clk_parent_apb); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_apb"); + ret = -EINVAL; + } + + name_parent = vpu_get_clk_src_name(s_vpu_drv_context.freq_div, clock_coda7l_apb_map); + clk_parent = clk_get(NULL, name_parent); + if ((!clk_parent )|| IS_ERR(clk_parent) ) { + printk(KERN_ERR "clock[%s]: failed to get parent in probe[%s] by clk_get()!\n", "clk_coda7_apb", name_parent); + ret = -EINVAL; + } else { + s_vpu_drv_context.clk_parent_apb= clk_parent; + } + + ret = clk_set_parent(s_vpu_drv_context.clk_coda7_apb, s_vpu_drv_context.clk_parent_apb); + if (ret) { + printk(KERN_ERR "clock[%s]: clk_set_parent() failed in probe!", "clk_coda7_apb"); + ret = -EINVAL; + } + + printk(KERN_ERR "vpu parent clock name %s, freq: %dHz\n", name_parent, (int)clk_get_rate(s_vpu_drv_context.clk_coda7_apb)); + + return ret; +} +static int vpu_set_mm_clk(void) +{ + int ret =0; + struct clk *clk_mm_i; + struct clk *clk_coda7_axi; + struct clk *clk_coda7_cc; + struct clk *clk_coda7_apb; + +#if defined(CONFIG_ARCH_SCX35) + +//Config for clk_mm_i +#ifdef CONFIG_OF + clk_mm_i = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_mm_i"); +#else + clk_mm_i = clk_get(NULL, "clk_mm_i"); +#endif + + if (IS_ERR(clk_mm_i) || (!clk_mm_i)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", + "clk_mm_i"); + printk(KERN_ERR "###: clk_mm_i = %p\n", clk_mm_i); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_mm_i= clk_mm_i; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_mm_i); + if (ret) { + printk(KERN_ERR "###:s_vpu_drv_context.clk_mm_i: clk_enable() failed!\n"); + return ret; + } +#endif + +//Config for clk_coda7_axi +#ifdef CONFIG_OF + clk_coda7_axi = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_coda7_axi"); +#else + clk_coda7_axi = clk_get(NULL, "clk_coda7_axi"); +#endif + + if (IS_ERR(clk_coda7_axi) || (!clk_coda7_axi)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", "clk_coda7_axi"); + printk(KERN_ERR "###: clk_coda7_axi = %p\n", clk_coda7_axi); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_coda7_axi = clk_coda7_axi; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_coda7_axi); + if (ret) { + printk(KERN_ERR "###: clk_coda7_axi: clk_enable() failed!\n"); + return ret; + } + +//Config for clk_coda7_cc +#ifdef CONFIG_OF + clk_coda7_cc = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_coda7_cc"); +#else + clk_coda7_cc = clk_get(NULL, "clk_coda7_cc"); +#endif + + if (IS_ERR(clk_coda7_cc) || (!clk_coda7_cc)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", "clk_coda7_cc"); + printk(KERN_ERR "###: clk_coda7_cc = %p\n", clk_coda7_cc); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_coda7_cc = clk_coda7_cc; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_coda7_cc); + if (ret) { + printk(KERN_ERR "###: clk_coda7_cc: clk_enable() failed!\n"); + return ret; + } + +//Config for clk_coda7_apb +#ifdef CONFIG_OF + clk_coda7_apb = of_clk_get_by_name(s_vpu_drv_context.dev_np, "clk_coda7_apb"); +#else + clk_coda7_apb = clk_get(NULL, "clk_coda7_apb"); +#endif + + if (IS_ERR(clk_coda7_apb) || (!clk_coda7_apb)) { + printk(KERN_ERR "###: Failed : Can't get clock [%s}!\n", "clk_coda7_cc"); + printk(KERN_ERR "###: clk_coda7_cc = %p\n", clk_coda7_apb); + ret = -EINVAL; + goto errout; + } else { + s_vpu_drv_context.clk_coda7_apb = clk_coda7_apb; + } + + ret = clk_prepare_enable(s_vpu_drv_context.clk_coda7_apb); + if (ret) { + printk(KERN_ERR "###: clk_coda7_apb: clk_enable() failed!\n"); + return ret; + } + + ret = vpu_set_parent_for_coda7l_clk(); + if(ret) { + printk(KERN_ERR "###:vpu set parent failed!\n"); + return ret; + } + + return 0; + +errout: + + vpu_clk_free(&s_vpu_drv_context); + + return ret; +} + +struct clk *vpu_clk_get(struct device *dev) +{ + return clk_get(dev, VPU_CLK_NAME); +} +void vpu_clk_put(struct clk *clk) +{ + if (!(clk == NULL || IS_ERR(clk))) + clk_put(clk); +} +int vpu_clk_enable(struct clk *clk) +{ + +#if 0 + if (!(clk == NULL || IS_ERR(clk))) { + /* the bellow is for C&M EVB.*/ + { + struct clk *s_vpuext_clk = NULL; + s_vpuext_clk = clk_get(NULL, "vcore"); + if (s_vpuext_clk) + { + vpu_logi("[VPUDRV] vcore clk=%p\n", s_vpuext_clk); + clk_enable(s_vpuext_clk); + } + + vpu_logi("[VPUDRV] vbus clk=%p\n", s_vpuext_clk); + if (s_vpuext_clk) + { + s_vpuext_clk = clk_get(NULL, "vbus"); + clk_enable(s_vpuext_clk); + } + } + + /* for C&M EVB. */ + return clk_enable(clk); + } +#endif + + return 0; +} + +void vpu_clk_disable(struct clk *clk) +{ +#if 0 + if (!(clk == NULL || IS_ERR(clk))) { + vpu_logd("[VPUDRV] vpu_clk_disable\n"); + clk_disable(clk); + } +#endif +} + + diff --git a/drivers/misc/sprd_coda7l/vpu.h b/drivers/misc/sprd_coda7l/vpu.h new file mode 100644 index 00000000..292b512f --- /dev/null +++ b/drivers/misc/sprd_coda7l/vpu.h @@ -0,0 +1,113 @@ +/* + * vpu.c + * + * linux device driver for VPU. + * + * Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library 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 Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __VPU_DRV_H__ +#define __VPU_DRV_H__ + +#include +#include + + +#define VDI_IOCTL_MAGIC 'V' +#define VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY _IO(VDI_IOCTL_MAGIC, 0) +#define VDI_IOCTL_FREE_PHYSICALMEMORY _IO(VDI_IOCTL_MAGIC, 1) +#define VDI_IOCTL_WAIT_INTERRUPT _IO(VDI_IOCTL_MAGIC, 2) +#define VDI_IOCTL_SET_CLOCK_GATE _IO(VDI_IOCTL_MAGIC, 3) +#define VDI_IOCTL_RESET _IO(VDI_IOCTL_MAGIC, 4) +#define VDI_IOCTL_GET_INSTANCE_POOL _IO(VDI_IOCTL_MAGIC, 5) +#define VDI_IOCTL_GET_COMMON_MEMORY _IO(VDI_IOCTL_MAGIC, 6) +#define VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO _IO(VDI_IOCTL_MAGIC, 8) +#define VDI_IOCTL_OPEN_INSTANCE _IO(VDI_IOCTL_MAGIC, 9) +#define VDI_IOCTL_CLOSE_INSTANCE _IO(VDI_IOCTL_MAGIC, 10) +#define VDI_IOCTL_GET_INSTANCE_NUM _IO(VDI_IOCTL_MAGIC, 11) + +// BIT_RUN command +enum { + SEQ_INIT = 1, + SEQ_END = 2, + PIC_RUN = 3, + SET_FRAME_BUF = 4, + ENCODE_HEADER = 5, + ENC_PARA_SET = 6, + DEC_RENEW = 6, + DEC_PARA_SET = 7, + DEC_BUF_FLUSH = 8, + RC_CHANGE_PARAMETER = 9, + VPU_SLEEP = 10, + VPU_WAKE = 11, + ENC_ROI_INIT = 12, + FIRMWARE_GET = 0xf +}; + +typedef struct vpudrv_buffer_t { + unsigned int size; + unsigned int phys_addr; + unsigned long base; /*kernel logical address in use kernel*/ + unsigned long virt_addr; /* virtual user space address */ +} vpudrv_buffer_t; + + +typedef struct vpu_bit_firmware_info_t { + unsigned int size; /* size of this structure */ + unsigned int core_idx; + unsigned int reg_base_offset; + unsigned short bit_code[512]; +} vpu_bit_firmware_info_t; + +typedef struct vpudrv_inst_info_t { + unsigned int core_idx; + unsigned int inst_idx; + int inst_open_count; /* for output only */ +} vpudrv_inst_info_t; + +struct vpu_dev { + unsigned int freq_div; + + struct semaphore vpu_mutex; + + struct clk *vpu_clk; + struct clk *vpu_parent_clk; + struct clk *mm_clk; + + unsigned int irq; + + struct vsp_fh *vpu_fp; + struct device_node *dev_np; +}; + + +#define VPU_DEBUG 1 + + +#define vpu_loge(fmt, ...) printk(KERN_ERR "[ERROR][" LOG_TAG "]" "[F:%s-L:%d]" fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define vpu_logw(fmt, ...) printk(KERN_WARNING "[WARN ][" LOG_TAG "]" "[F:%s-L:%d]" fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define vpu_logi(fmt, ...) printk(KERN_WARNING "[INFO ][" LOG_TAG "]" "[F:%s-L:%d]" fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#if VPU_DEBUG +#define vpu_logd(fmt, ...) printk(KERN_WARNING "[DEBUG][" LOG_TAG "]" "[F:%s-L:%d]" fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#else +#define vpu_logd(fmt, ...) +#endif + + +#endif diff --git a/drivers/misc/sprd_coda7l/vpuconfig.h b/drivers/misc/sprd_coda7l/vpuconfig.h new file mode 100644 index 00000000..f00b4960 --- /dev/null +++ b/drivers/misc/sprd_coda7l/vpuconfig.h @@ -0,0 +1,163 @@ +#ifndef _VPU_CONFIG_H_ +#define _VPU_CONFIG_H_ + +//#include "../config.h" +//#include "vputypes.h" + + + +#define MAX_INST_HANDLE_SIZE (20*1024) + +#define BODA7503_CODE 0x7503 +#define CODA7L_CODE 0x7542 +#define CODA851_CODE 0x8510 +#define BODA950_CODE 0x9500 +#define CODA960_CODE 0x9600 +#define CODA980_CODE 0x9800 +#define WAVE320_CODE 0x3200 + +# define MAX_ENC_PIC_WIDTH 1920 +# define MAX_ENC_PIC_HEIGHT 1088 +# define MAX_ENC_AVC_PIC_WIDTH 1920 +# define MAX_ENC_AVC_PIC_HEIGHT 1088 +# define MAX_DEC_PIC_WIDTH 1920 +# define MAX_DEC_PIC_HEIGHT 1088 +#define MIN_ENC_PIC_WIDTH 96 +#define MIN_ENC_PIC_HEIGHT 16 + +// MAX_NUM_INSTANCE can set any value as many as memory size of system. the bellow setting is dedicated for C&M FPGA board. +#define MAX_NUM_INSTANCE 4 +#define MAX_NUM_VPU_CORE 1 + +#if MAX_DEC_PIC_WIDTH > 1920 // 4K + +#define USE_WTL 0 // not support 4K WTL +#define MAX_EXTRA_FRAME_BUFFER_NUM 1 +#define MAX_PP_SRC_NUM 2 //PPU buffer(decoding case) or Source buffer(encoding case) + +#define MAX_DPB_MBS 110400 // 32768 for level 5 + +#elif MAX_DEC_PIC_WIDTH > 720 + +#define USE_WTL 0 // for WTL (needs MAX_DPB_NUM*2 frame) +#define MAX_EXTRA_FRAME_BUFFER_NUM 4 +#define MAX_PP_SRC_NUM 2 //PPU buffer(decoding case) or Source buffer(encoding case) + +#define MAX_DPB_MBS 32768 // 32768 for level 4.1 + +#else + +#define USE_WTL 0 // for WTL (needs MAX_DPB_NUM*2 frame) +#define MAX_EXTRA_FRAME_BUFFER_NUM 4 +#define MAX_PP_SRC_NUM 2 //PPU buffer(decoding case) or Source buffer(encoding case) + +#define MAX_DPB_MBS 32768 // 32768 for level 4.1 + +#endif + + +// codec specific configuration +#define VPU_REORDER_ENABLE 1 // it can be set to 1 to handle reordering DPB in host side. +#define VPU_GMC_PROCESS_METHOD 1 +#define VPU_AVC_X264_SUPPORT 1 + +// DRAM configuration for TileMap access +#ifdef CNM_FPGA_PLATFORM +#define EM_RAS 13 +#define EM_BANK 2 +#define EM_CAS 9 +#define EM_WIDTH 3 +#else +#define EM_RAS 13 +#define EM_BANK 3 +#define EM_CAS 10 +#define EM_WIDTH 2 +#endif + + +// Application specific configuration +#define VPU_BUSY_CHECK_TIMEOUT 5000 + +#ifdef CNM_FPGA_PLATFORM +#ifdef CNM_FPGA_USB_INTERFACE +#define VPU_FRAME_ENDIAN VDI_LITTLE_ENDIAN +#define VPU_STREAM_ENDIAN VDI_LITTLE_ENDIAN +#else +#define VPU_FRAME_ENDIAN VDI_BIG_ENDIAN +#define VPU_STREAM_ENDIAN VDI_BIG_ENDIAN +#endif +#else +#define VPU_FRAME_ENDIAN VDI_LITTLE_ENDIAN +#define VPU_STREAM_ENDIAN VDI_LITTLE_ENDIAN +#endif +#define VPU_USER_DATA_ENDIAN VDI_LITTLE_ENDIAN + +#define CBCR_INTERLEAVE 1 //[default 1 for BW checking with CnMViedo Conformance] 0 (chroma separate mode), 1 (chroma interleave mode) // if the type of tiledmap uses the kind of MB_RASTER_MAP. must set to enable CBCR_INTERLEAVE +#define VPU_REPORT_USERDATA 0 + + + + + + +#define USE_BIT_INTERNAL_BUF 1 +#define USE_IP_INTERNAL_BUF 1 +#define USE_DBKY_INTERNAL_BUF 1 +#define USE_DBKC_INTERNAL_BUF 1 +#define USE_OVL_INTERNAL_BUF 1 +#define USE_ME_INTERNAL_BUF 1 +#define USE_SCALER_INTERNAL_BUF 1 + + +#define VPU_GBU_SIZE 1024 //No modification required +#define JPU_GBU_SIZE 512 //No modification required + +//********************************************// +// External Memory Map Table - No modification required +//********************************************// + + +# define CODE_BUF_SIZE (248*1024) +# define MINI_PIPPEN_SCALER_COEFFCIENT_ARRAY_SIZE 256 /** the real size is 152 bytes, please refer to: ScalerCoeff_c */ +# define WORK_BUF_SIZE (512*1024) + (MAX_NUM_INSTANCE*48*1024) + (MAX_DEC_PIC_WIDTH*MAX_DEC_PIC_HEIGHT*3/4) + MINI_PIPPEN_SCALER_COEFFCIENT_ARRAY_SIZE + + +#define PARA_BUF_SIZE ( 10 * 1024 ) + + +//=====1. VPU COMMON MEMORY ======================// +#define SIZE_COMMON (CODE_BUF_SIZE + WORK_BUF_SIZE + PARA_BUF_SIZE) + +//=====2. VPU BITSTREAM MEMORY ===================// +#define MAX_STREAM_BUF_SIZE 0x300000 // max bitstream size + +//===== 3. VPU PPS Save Size ===================// +//----- SPS/PPS save buffer --------------------// +#define PS_SAVE_SIZE (512*1024) +//----- VP8 MB save buffer -------------------// +#define VP8_MB_SAVE_SIZE (17*4*(MAX_DEC_PIC_WIDTH*MAX_DEC_PIC_HEIGHT/256)) // MB information + split MVs)*4*MbNumbyte +//----- slice save buffer --------------------// +#define SLICE_SAVE_SIZE (MAX_DEC_PIC_WIDTH*MAX_DEC_PIC_HEIGHT*3/4) // this buffer for ASO/FMO + + +//=====4. VPU REPORT MEMORY ======================// +#define SIZE_REPORT_BUF (0x34000) + +//=====5. VPU Encoder Scratch buffer ssize ======================// +#define SIZE_AVCENC_QMAT_TABLE ((16*6)+(64*2)) // 4x4 6, 8x8 2 +#define SIZE_MP4ENC_DATA_PARTITION (MAX_ENC_PIC_WIDTH*MAX_ENC_PIC_HEIGHT*3/4) + +//=====6. Check VPU required memory size =======================// +#define MAX_FRM_SIZE ((MAX_DEC_PIC_WIDTH*MAX_DEC_PIC_HEIGHT*3)/2) +#define MAX_DEC_FRAME_BUFFERING (MAX_DPB_MBS/((MAX_DEC_PIC_WIDTH*MAX_DEC_PIC_HEIGHT)/256)) +#define MAX_DPB_NUM (16 < MAX_DEC_FRAME_BUFFERING ? (16+2+MAX_EXTRA_FRAME_BUFFER_NUM) : (MAX_DEC_FRAME_BUFFERING+2+MAX_EXTRA_FRAME_BUFFER_NUM)) // (+2 for current and display_delay) +#define MAX_DPB_SIZE (((MAX_FRM_SIZE+0x3fff)&(~0x3fff))*MAX_DPB_NUM) // frame buffer for codec (MAX_DPB_NUM) +#define MAX_MV_COL_SIZE (((MAX_FRM_SIZE+4)/5)*MAX_DPB_NUM) +#define MAX_PP_SRC_SIZE (((MAX_FRM_SIZE+0x3fff)&(~0x3fff))*MAX_PP_SRC_NUM) + +#define CODEC_FRAME_BASE ((((MAX_STREAM_BUF_SIZE*MAX_NUM_INSTANCE)) + 0xff) & (~0xff)) +#define REQUIRED_VPU_MEMORY_SIZE (CODEC_FRAME_BASE+((MAX_DPB_SIZE*(1+USE_WTL))+MAX_MV_COL_SIZE+MAX_PP_SRC_SIZE)*MAX_NUM_INSTANCE) + + +#endif /* _VPU_CONFIG_H_ */ + -- cgit v1.3.1