Skip to content

Instantly share code, notes, and snippets.

@nickfox-taterli
Created December 18, 2025 08:41
Show Gist options
  • Select an option

  • Save nickfox-taterli/20864b1646a17911c19d752a28af3247 to your computer and use it in GitHub Desktop.

Select an option

Save nickfox-taterli/20864b1646a17911c19d752a28af3247 to your computer and use it in GitHub Desktop.
Allwinner Cedar 视频引擎驱动程序
/*
* drivers\media\cedar_ve
* (C) Copyright 2010-2016
* Reuuimlla Technology Co., Ltd. <www.allwinnertech.com>
* fangning<fangning@allwinnertech.com>
*
* Allwinner Cedar 视频引擎驱动程序
*
* 本驱动程序实现了 Allwinner 平台的 Cedar 视频引擎的 Linux 内核驱动,
* 支持硬件视频编解码加速,包括 H.264、HEVC、MPEG、VC-1、RMVB 等格式。
*
* 主要功能:
* - 视频解码器硬件加速支持
* - 视频编码器硬件加速支持
* - JPEG 解码硬件加速支持
* - 时钟和电源管理
* - 中断处理和任务调度
* - 内存映射和 DMA 操作
* - 设备文件接口(/dev/cedar_dev)
*
* 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.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/preempt.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/rmap.h>
#include <linux/wait.h>
#include <linux/semaphore.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <asm/siginfo.h>
#include <asm/signal.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
#include <linux/reset.h>
#include <linux/sched/signal.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
#include <linux/clk/sunxi.h>
#endif /*LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include "cedar_ve.h"
#include <linux/regulator/consumer.h>
#include <linux/of_reserved_mem.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,1,0)
#include <linux/soc/sunxi/sunxi_sram.h>
#endif
/*
* 配置宏定义
*/
#define MMAP_UNCACHABLE /* 启用不可缓存内存映射 */
#ifndef MMAP_UNCACHABLE
extern int flush_clean_user_range(long start, long end);
#endif
struct regulator *regu; /* 电压调节器指针 */
/*
* 功能开关宏定义
*/
//#define USE_ION /* 使用 ION 内存管理(当前未启用)*/
//#define CEDAR_DEBUG /* 调试信息输出开关(当前未启用)*/
#define HAVE_TIMER_SETUP /* 使用新版本的 timer_setup API */
#define DRV_VERSION "0.01beta" /* 驱动版本号 */
#undef USE_CEDAR_ENGINE /* 未使用 Cedar 引擎任务调度 */
/*
* 设备号配置
*/
#ifndef CEDARDEV_MAJOR
#define CEDARDEV_MAJOR (150) /* 主设备号默认值 */
#endif
#ifndef CEDARDEV_MINOR
#define CEDARDEV_MINOR (0) /* 次设备号默认值 */
#endif
/*
* 硬件寄存器基址
*/
#define MACC_REGS_BASE (0x01C0E000) /* Media Accelerator 寄存器基址 */
#ifndef CONFIG_OF
//H3: arch/arm/mach-sunxi/include/mach/sun8i/irqs-sun8iw7p1.h
//#define SUNXI_IRQ_VE (90) /* VE 中断号(非设备树模式)*/
#endif
/*
* 调试和日志宏
*/
#define cedar_ve_printk(level, msg...) printk(level "cedar_ve: " msg)
/*
* 时钟频率配置(单位:MHz)
*/
#define VE_CLK_HIGH_WATER (700) /* 时钟频率上限:700MHz */
#define VE_CLK_LOW_WATER (100) /* 时钟频率下限:100MHz */
/*
* 全局变量和模块参数
*/
static int g_dev_major = CEDARDEV_MAJOR; /* 主设备号(可通过模块参数设置)*/
static int g_dev_minor = CEDARDEV_MINOR; /* 次设备号(可透过模块参数设置)*/
module_param(g_dev_major, int, S_IRUGO); /* 主设备号模块参数,只读权限 */
module_param(g_dev_minor, int, S_IRUGO); /* 次设备号模块参数,只读权限 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
/*
* 4.11.0 及以上版本的系统控制器寄存器定义
*/
#define SYSCON_SRAM_CTRL_REG0 0x0 /* SRAM 控制寄存器 0 */
#define SYSCON_SRAM_C1_MAP_VE 0x7fffffff /* SRAM C1 映射到 VE 的掩码 */
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
/*
* 4.11.0 以下版本的时钟结构体定义
*/
struct clk *ve_moduleclk = NULL; /* VE 模块时钟 */
struct clk *ve_parent_pll_clk = NULL; /* VE 父 PLL 时钟 */
struct clk *ve_power_gating = NULL; /* VE 电源门控时钟 */
static u32 ve_parent_clk_rate = 300000000; /* VE 父时钟默认频率:300MHz */
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
/*
* IO 映射参数结构体
*/
struct iomap_para{
volatile char* regs_macc; /* MACC 寄存器映射地址 */
volatile char* regs_avs; /* AVS 寄存器映射地址 */
};
/*
* VE 等待队列头声明
*/
static DECLARE_WAIT_QUEUE_HEAD(wait_ve);
/*
* Cedar 设备结构体 - 包含所有 VE 相关的状态和控制信息
*/
struct cedar_dev {
struct cdev cdev; /* 字符设备结构体 */
struct platform_device *pdev; /* 平台设备指针 */
struct device *dev; /* 设备指针 */
struct device *odev; /* 类设备指针 */
struct class *class; /* 设备类,用于自动创建设备节点 */
struct semaphore sem; /* 互斥信号量,用于保护关键资源 */
wait_queue_head_t wq; /* 轮询操作的等待队列 */
struct iomap_para iomap_addrs; /* IO 映射地址信息 */
struct timer_list cedar_engine_timer; /* Cedar 引擎主定时器 */
struct timer_list cedar_engine_timer_rel; /* Cedar 引擎释放定时器 */
u32 irq; /* Cedar 视频引擎中断号 */
u32 de_irq_flag; /* 视频解码器中断标志 */
u32 de_irq_value; /* 视频解码器中断值 */
u32 en_irq_flag; /* 视频编码器中断标志 */
u32 en_irq_value; /* 视频编码器中断值 */
u32 irq_has_enable; /* 中断使能状态标志 */
u32 ref_count; /* 引用计数,用于电源管理 */
u32 jpeg_irq_flag; /* JPEG 解码中断标志 */
u32 jpeg_irq_value; /* JPEG 解码中断值 */
volatile u32* sram_bass_vir; /* SRAM 基地址虚拟指针 */
volatile u32* clk_bass_vir; /* 时钟基地址虚拟指针 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
struct clk *mod_clk; /* 模块时钟 */
struct clk *ahb_clk; /* AHB 总线时钟 */
struct clk *ram_clk; /* RAM 时钟 */
struct reset_control *rstc; /* 复位控制 */
struct regmap *syscon; /* 系统控制器寄存器映射 */
#endif /*LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)*/
unsigned long ve_start; /* VE 内存区域起始物理地址 */
unsigned long ve_size; /* VE 内存区域大小 */
void *ve_start_virt; /* VE 内存区域起始虚拟地址 */
resource_size_t ve_start_pa; /* VE 内存区域物理地址(resource_size_t 类型)*/
};
/*
* VE 设备信息结构体
*/
struct ve_info {
u32 set_vol_flag; /* 电压设置标志 */
};
/*
* 全局变量声明
*/
struct cedar_dev *cedar_devp; /* Cedar 设备结构体全局指针 */
struct file *ve_file; /* 当前打开的 VE 文件指针 */
u32 int_sta=0, int_value; /* 中断状态和值的全局变量 */
/*
* 设备树匹配表 - 用于平台设备识别
*/
#if defined(CONFIG_OF)
static struct of_device_id sunxi_cedar_ve_match[] = {
{ .compatible = "allwinner,sunxi-cedar-ve", },
//{ .compatible = "allwinner,sun8i-h3-video-engine", },
{}
};
MODULE_DEVICE_TABLE(of, sunxi_cedar_ve_match);
#endif
/*
* 安全监控模式下的寄存器访问函数
* 支持 TrustZone 安全环境下的寄存器读写
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || ((defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN8IW9P1))
#if defined(CONFIG_SUNXI_TRUSTZONE)
/* 安全模式下的寄存器读取函数 */
static inline u32 sunxi_smc_readl(void __iomem *addr)
{
if (sunxi_soc_is_secure()) {
return call_firmware_op(read_reg, addr);
} else {
return readl(addr);
}
}
/* 安全模式下的寄存器写入函数 */
static inline void sunxi_smc_writel(u32 val, void __iomem *addr)
{
if (sunxi_soc_is_secure()) {
call_firmware_op(write_reg, val, addr);
} else {
writel(val, addr);
}
}
#else
/* 普通模式下的寄存器读取函数 */
static inline u32 sunxi_smc_readl(void __iomem *addr)
{
return readl(addr);
}
/* 普通模式下的寄存器写入函数 */
static inline void sunxi_smc_writel(u32 val, void __iomem *addr)
{
writel(val, addr);
}
#endif
#endif /*((defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN8IW9P1))*/
/*
* Cedar 视频引擎中断服务例程
* 功能:处理视频编解码硬件中断,唤醒等待队列
* 参数:irq - 中断号,dev - 设备指针(未使用)
* 返回值:IRQ_HANDLED 表示中断已处理
*/
static irqreturn_t VideoEngineInterupt(int irq, void *dev)
{
ulong ve_int_status_reg;
ulong ve_int_ctrl_reg;
u32 status = 0;
volatile int val;
int modual_sel = 0;
u32 interrupt_enable = 0;
struct iomap_para addrs = cedar_devp->iomap_addrs;
modual_sel = readl(addrs.regs_macc + 0);
//printk("!!!!!!!!! [cedar ISR]: modual_sel %08x\n", modual_sel);
//return IRQ_HANDLED;
//001300cb
//001300c1
/* ENCODER (EN) case for VE 1633 and newer */
if ((modual_sel&(3<<6)) || (modual_sel == 0xB)) {
if (modual_sel == 0xB) {
/*avc enc*/
ve_int_status_reg = (ulong)(addrs.regs_macc + 0xb00 + 0x1c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xb00 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) &(0x7);
status = readl((void*)ve_int_status_reg);
status &= 0xf;
} else {
modual_sel &= ~(0xF);
if (modual_sel&(1<<7)) {
/*avc enc*/
//printk("[cedrus]: AVC interrupt!!!\n");
ve_int_status_reg = (ulong)(addrs.regs_macc + 0xb00 + 0x1c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xb00 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) &(0x7);
status = readl((void*)ve_int_status_reg);
status &= 0xf;
} else {
/*isp*/
ve_int_status_reg = (ulong)(addrs.regs_macc + 0xa00 + 0x10);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xa00 + 0x08);
interrupt_enable = readl((void*)ve_int_ctrl_reg) &(0x1);
status = readl((void*)ve_int_status_reg);
status &= 0x1;
}
}
/*modify by fangning 2013-05-22*/
if (status && interrupt_enable) {
/*disable interrupt*/
if (modual_sel == 0xB) {
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xb00 + 0x14);
val = readl((void*)ve_int_ctrl_reg);
writel(val & (~0xf), (void*)ve_int_ctrl_reg);
} else {
/*avc enc*/
if (modual_sel&(1<<7)) {
//printk("[cedrus]: Read AVC interrupt!!!\n");
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xb00 + 0x14);
val = readl((void*)ve_int_ctrl_reg);
writel(val & (~0x7), (void*)ve_int_ctrl_reg);
} else {
/*isp*/
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xa00 + 0x08);
val = readl((void*)ve_int_ctrl_reg);
writel(val & (~0x1), (void*)ve_int_ctrl_reg);
}
}
/*hx modify 2011-8-1 16:08:47*/
cedar_devp->en_irq_value = 1;
cedar_devp->en_irq_flag = 1;
/*any interrupt will wake up wait queue*/
//printk("[cedrus]: Video interrupt occurs EN!!!\n");
wake_up_interruptible(&wait_ve);
}
return IRQ_HANDLED;
}
#if ((defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN50I))
if (modual_sel&(0x20)) {
ve_int_status_reg = (ulong)(addrs.regs_macc + 0xe00 + 0x1c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0xe00 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0x38);
status = readl((void*)ve_int_status_reg);
if ((status&0x7) && interrupt_enable) {
/*disable interrupt*/
val = readl((void*)ve_int_ctrl_reg);
writel(val & (~0x38), (void*)ve_int_ctrl_reg);
cedar_devp->jpeg_irq_value = 1;
cedar_devp->jpeg_irq_flag = 1;
/*any interrupt will wake up wait queue*/
wake_up_interruptible(&wait_ve);
}
}
#endif
/* DECODER (DE) case for all VE versions */
modual_sel &= 0xf;
if((modual_sel<=4) /*|| (modual_sel == 0xB)*/) {
/*estimate Which video format*/
switch (modual_sel)
{
case 0: /*mpeg124*/
ve_int_status_reg = (ulong)
(addrs.regs_macc + 0x100 + 0x1c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0x100 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0x7c);
break;
case 1: /*h264*/
ve_int_status_reg = (ulong)
(addrs.regs_macc + 0x200 + 0x28);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0x200 + 0x20);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0xf);
break;
case 2: /*vc1*/
ve_int_status_reg = (ulong)(addrs.regs_macc +
0x300 + 0x2c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0x300 + 0x24);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0xf);
break;
case 3: /*rmvb*/
ve_int_status_reg = (ulong)
(addrs.regs_macc + 0x400 + 0x1c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0x400 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0xf);
break;
case 4: /*hevc*/
ve_int_status_reg = (ulong)
(addrs.regs_macc + 0x500 + 0x38);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0x500 + 0x30);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0xf);
break;
#if 0
case 0xB: /*AVC (h264 encoder)*/
ve_int_status_reg = (unsigned int)(addrs.regs_macc + 0xb00 + 0x1c);
ve_int_ctrl_reg = (unsigned int)(addrs.regs_macc + 0xb00 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) &(0x7);
break;
#endif
default:
ve_int_status_reg = (ulong)(addrs.regs_macc + 0x100 + 0x1c);
ve_int_ctrl_reg = (ulong)(addrs.regs_macc + 0x100 + 0x14);
interrupt_enable = readl((void*)ve_int_ctrl_reg) & (0xf);
cedar_ve_printk(KERN_WARNING, "ve mode :%x "
"not defined!\n", modual_sel);
break;
}
status = readl((void*)ve_int_status_reg);
/*modify by fangning 2013-05-22*/
if ((status&0xf) && interrupt_enable) {
/*disable interrupt*/
if (modual_sel == 0) {
val = readl((void*)ve_int_ctrl_reg);
writel(val & (~0x7c), (void*)ve_int_ctrl_reg);
} else {
val = readl((void*)ve_int_ctrl_reg);
writel(val & (~0xf), (void*)ve_int_ctrl_reg);
}
cedar_devp->de_irq_value = 1;
cedar_devp->de_irq_flag = 1;
/*any interrupt will wake up wait queue*/
wake_up_interruptible(&wait_ve);
}
}
//printk("%08X\n", modual_sel);
return IRQ_HANDLED;
}
/*
* 全局状态变量
*/
static int clk_status = 0; /* VE 时钟状态:0=关闭,1=开启 */
/*
* 任务管理相关的链表和锁
*/
static LIST_HEAD(run_task_list); /* 运行中的任务链表头 */
static LIST_HEAD(del_task_list); /* 待删除的任务链表头 */
static spinlock_t cedar_spin_lock; /* Cedar 任务管理的自旋锁 */
/*
* 任务管理相关常量定义
*/
#define CEDAR_RUN_LIST_NONULL -1 /* 任务列表非空的返回值 */
#define CEDAR_NONBLOCK_TASK 0 /* 非阻塞任务模式 */
#define CEDAR_BLOCK_TASK 1 /* 阻塞任务模式 */
#define CLK_REL_TIME 10000 /* 时钟释放延迟时间(毫秒)*/
#define TIMER_CIRCLE 50 /* 定时器循环间隔(毫秒)*/
/*
* 任务状态定义
*/
#define TASK_INIT 0x00 /* 任务初始状态 */
#define TASK_TIMEOUT 0x55 /* 任务超时状态 */
#define TASK_RELEASE 0xaa /* 任务释放状态 */
/*
* Cedar 专用信号定义
*/
#define SIG_CEDAR 35 /* Cedar 任务管理信号 */
static int enable_cedar_hw_clk(void)
{
ulong flags;
int res = -EFAULT;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (clk_status == 1)
goto out;
clk_status = 1;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
reset_control_deassert(cedar_devp->rstc);
if (clk_enable(cedar_devp->mod_clk)) { cedar_ve_printk(KERN_WARNING, "try to enable ve_moduleclk failed! (%d=\n", __LINE__); goto out; }
res = 0;
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
sunxi_periph_reset_deassert(ve_moduleclk);
if (clk_enable(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, "enable ve_moduleclk failed;\n");
goto out;
}else {
res = 0;
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
#ifdef CEDAR_DEBUG
printk("%s,%d\n",__func__,__LINE__);
#endif
out:
spin_unlock_irqrestore(&cedar_spin_lock, flags);
return res;
}
int disable_cedar_hw_clk(void)
{
ulong flags;
int res = -EFAULT;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (clk_status == 0) {
res = 0;
goto out;
}
clk_status = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if ((NULL == cedar_devp->mod_clk)||(IS_ERR(cedar_devp->mod_clk))) {
cedar_ve_printk(KERN_WARNING, "ve_moduleclk is invalid\n");
} else {
clk_disable(cedar_devp->mod_clk);
reset_control_assert(cedar_devp->rstc);
res = 0;
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if ((NULL == ve_moduleclk)||(IS_ERR(ve_moduleclk))) {
cedar_ve_printk(KERN_WARNING, "ve_moduleclk is invalid\n");
} else {
clk_disable(ve_moduleclk);
sunxi_periph_reset_assert(ve_moduleclk);
res = 0;
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
#ifdef CEDAR_DEBUG
printk("%s,%d\n",__func__,__LINE__);
#endif
out:
spin_unlock_irqrestore(&cedar_spin_lock, flags);
return res;
}
void cedardev_insert_task(struct cedarv_engine_task* new_task)
{
struct cedarv_engine_task *task_entry;
ulong flags;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (list_empty(&run_task_list))
new_task->is_first_task = 1;
list_for_each_entry(task_entry, &run_task_list, list) {
if ((task_entry->is_first_task == 0) && (task_entry->running == 0) && (task_entry->t.task_prio < new_task->t.task_prio)) {
break;
}
}
list_add(&new_task->list, task_entry->list.prev);
#ifdef CEDAR_DEBUG
printk("%s,%d, TASK_ID:",__func__,__LINE__);
list_for_each_entry(task_entry, &run_task_list, list) {
printk("%d!", task_entry->t.ID);
}
printk("\n");
#endif
mod_timer(&cedar_devp->cedar_engine_timer, jiffies + 0);
spin_unlock_irqrestore(&cedar_spin_lock, flags);
}
int cedardev_del_task(int task_id)
{
struct cedarv_engine_task *task_entry;
ulong flags;
spin_lock_irqsave(&cedar_spin_lock, flags);
list_for_each_entry(task_entry, &run_task_list, list) {
if (task_entry->t.ID == task_id && task_entry->status != TASK_RELEASE) {
task_entry->status = TASK_RELEASE;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
mod_timer(&cedar_devp->cedar_engine_timer, jiffies + 0);
return 0;
}
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
return -1;
}
int cedardev_check_delay(int check_prio)
{
struct cedarv_engine_task *task_entry;
int timeout_total = 0;
ulong flags;
spin_lock_irqsave(&cedar_spin_lock, flags);
list_for_each_entry(task_entry, &run_task_list, list) {
if ((task_entry->t.task_prio >= check_prio) || (task_entry->running == 1) || (task_entry->is_first_task == 1))
timeout_total = timeout_total + task_entry->t.frametime;
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
#ifdef CEDAR_DEBUG
printk("%s,%d,%d\n", __func__, __LINE__, timeout_total);
#endif
return timeout_total;
}
#ifdef HAVE_TIMER_SETUP
static void cedar_engine_for_timer_rel(struct timer_list *t)
#else
static void cedar_engine_for_timer_rel(unsigned long arg)
#endif
{
ulong flags;
int ret = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (list_empty(&run_task_list)) {
ret = disable_cedar_hw_clk();
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "clk disable error!\n");
}
} else {
cedar_ve_printk(KERN_WARNING, "clk disable time out "
"but task left\n");
mod_timer( &cedar_devp->cedar_engine_timer, jiffies + msecs_to_jiffies(TIMER_CIRCLE));
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
}
#ifdef HAVE_TIMER_SETUP
static void cedar_engine_for_events(struct timer_list *t)
#else
static void cedar_engine_for_events(unsigned long arg)
#endif
{
struct cedarv_engine_task *task_entry, *task_entry_tmp;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)
struct kernel_siginfo info;
#else
struct siginfo info;
#endif
ulong flags;
spin_lock_irqsave(&cedar_spin_lock, flags);
list_for_each_entry_safe(task_entry, task_entry_tmp, &run_task_list, list) {
mod_timer(&cedar_devp->cedar_engine_timer_rel, jiffies + msecs_to_jiffies(CLK_REL_TIME));
if (task_entry->status == TASK_RELEASE ||
time_after(jiffies, task_entry->t.timeout)) {
if (task_entry->status == TASK_INIT)
task_entry->status = TASK_TIMEOUT;
list_move(&task_entry->list, &del_task_list);
}
}
list_for_each_entry_safe(task_entry, task_entry_tmp, &del_task_list, list) {
info.si_signo = SIG_CEDAR;
info.si_code = task_entry->t.ID;
if (task_entry->status == TASK_TIMEOUT) {
info.si_errno = TASK_TIMEOUT;
send_sig_info(SIG_CEDAR, &info, task_entry->task_handle);
} else if (task_entry->status == TASK_RELEASE) {
info.si_errno = TASK_RELEASE;
send_sig_info(SIG_CEDAR, &info, task_entry->task_handle);
}
list_del(&task_entry->list);
kfree(task_entry);
}
if (!list_empty(&run_task_list)) {
task_entry = list_entry(run_task_list.next, struct cedarv_engine_task, list);
if (task_entry->running == 0) {
task_entry->running = 1;
info.si_signo = SIG_CEDAR;
info.si_code = task_entry->t.ID;
info.si_errno = TASK_INIT;
send_sig_info(SIG_CEDAR, &info, task_entry->task_handle);
}
mod_timer( &cedar_devp->cedar_engine_timer, jiffies + msecs_to_jiffies(TIMER_CIRCLE));
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
}
#ifdef CONFIG_COMPAT
static long compat_cedardev_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{
int ret = 0;
int ve_timeout = 0;
/*struct cedar_dev *devp;*/
#ifdef USE_CEDAR_ENGINE
int rel_taskid = 0;
struct __cedarv_task task_ret;
struct cedarv_engine_task *task_ptr = NULL;
#endif
ulong flags;
struct ve_info *info;
info = filp->private_data;
switch (cmd)
{
case IOCTL_ENGINE_REQ:
#ifdef USE_CEDAR_ENGINE
if (copy_from_user(&task_ret, (void __user *)arg,
sizeof(struct __cedarv_task))) {
cedar_ve_printk(KERN_WARNING, "USE_CEDAR_ENGINE "
"copy_from_user fail\n");
return -EFAULT;
}
spin_lock_irqsave(&cedar_spin_lock, flags);
if (!list_empty(&run_task_list) &&
(task_ret.block_mode == CEDAR_NONBLOCK_TASK)) {
spin_unlock_irqrestore(&cedar_spin_lock, flags);
return CEDAR_RUN_LIST_NONULL;
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
task_ptr = kmalloc(sizeof(struct cedarv_engine_task), GFP_KERNEL);
if (!task_ptr) {
cedar_ve_printk(KERN_WARNING, "get "
"task_ptr error\n");
return PTR_ERR(task_ptr);
}
task_ptr->task_handle = current;
task_ptr->t.ID = task_ret.ID;
/*ms to jiffies*/
task_ptr->t.timeout = jiffies +
msecs_to_jiffies(1000*task_ret.timeout);
task_ptr->t.frametime = task_ret.frametime;
task_ptr->t.task_prio = task_ret.task_prio;
task_ptr->running = 0;
task_ptr->is_first_task = 0;
task_ptr->status = TASK_INIT;
cedardev_insert_task(task_ptr);
ret = enable_cedar_hw_clk();
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "IOCTL_ENGINE_REQ "
"clk enable error!\n");
return -EFAULT;
}
return task_ptr->is_first_task;
#else
cedar_devp->ref_count++;
if (1 == cedar_devp->ref_count)
enable_cedar_hw_clk();
break;
#endif
case IOCTL_ENGINE_REL:
#ifdef USE_CEDAR_ENGINE
rel_taskid = (int)arg;
ret = cedardev_del_task(rel_taskid);
#else
cedar_devp->ref_count--;
if (0 == cedar_devp->ref_count) {
ret = disable_cedar_hw_clk();
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "IOCTL_ENGINE_REL "
"clk disable error!\n");
return -EFAULT;
}
}
#endif
return ret;
case IOCTL_ENGINE_CHECK_DELAY:
{
struct cedarv_engine_task_info task_info;
if (copy_from_user(&task_info, (void __user *)arg,
sizeof(struct cedarv_engine_task_info))) {
cedar_ve_printk(KERN_WARNING, "%d "
"copy_from_user fail\n",
IOCTL_ENGINE_CHECK_DELAY);
return -EFAULT;
}
task_info.total_time = cedardev_check_delay(task_info.task_prio);
#ifdef CEDAR_DEBUG
printk("%s,%d,%d\n", __func__, __LINE__, task_info.total_time);
#endif
task_info.frametime = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (!list_empty(&run_task_list)) {
struct cedarv_engine_task *task_entry;
#ifdef CEDAR_DEBUG
printk("%s,%d\n",__func__,__LINE__);
#endif
task_entry = list_entry(run_task_list.next, struct cedarv_engine_task, list);
if (task_entry->running == 1)
task_info.frametime = task_entry->t.frametime;
#ifdef CEDAR_DEBUG
printk("%s,%d,%d\n",__func__,__LINE__,task_info.frametime);
#endif
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
if (copy_to_user((void *)arg, &task_info, sizeof(struct cedarv_engine_task_info))){
cedar_ve_printk(KERN_WARNING, "%d "
"copy_to_user fail\n",
IOCTL_ENGINE_CHECK_DELAY);
return -EFAULT;
}
}
break;
case IOCTL_WAIT_VE_DE:
ve_timeout = (int)arg;
cedar_devp->de_irq_value = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (cedar_devp->de_irq_flag)
cedar_devp->de_irq_value = 1;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
wait_event_interruptible_timeout(wait_ve, cedar_devp->de_irq_flag, ve_timeout*HZ);
cedar_devp->de_irq_flag = 0;
return cedar_devp->de_irq_value;
case IOCTL_WAIT_VE_EN:
ve_timeout = (int)arg;
cedar_devp->en_irq_value = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (cedar_devp->en_irq_flag)
cedar_devp->en_irq_value = 1;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
wait_event_interruptible_timeout(wait_ve, cedar_devp->en_irq_flag, ve_timeout*HZ);
cedar_devp->en_irq_flag = 0;
return cedar_devp->en_irq_value;
#if ((defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN50I))
case IOCTL_WAIT_JPEG_DEC:
ve_timeout = (int)arg;
cedar_devp->jpeg_irq_value = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (cedar_devp->jpeg_irq_flag)
cedar_devp->jpeg_irq_value = 1;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
wait_event_interruptible_timeout(wait_ve, cedar_devp->jpeg_irq_flag, ve_timeout*HZ);
cedar_devp->jpeg_irq_flag = 0;
return cedar_devp->jpeg_irq_value;
#endif
case IOCTL_ENABLE_VE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if (clk_prepare_enable(cedar_devp->mod_clk)) {
cedar_ve_printk(KERN_WARNING, "IOCTL_ENABLE_VE enable ve_moduleclk failed! (%d)\n", __LINE__);
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if (clk_prepare_enable(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, "IOCTL_ENABLE_VE "
"enable ve_moduleclk failed!\n");
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
break;
case IOCTL_DISABLE_VE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if ((NULL == cedar_devp->mod_clk)||IS_ERR(cedar_devp->mod_clk)) {
cedar_ve_printk(KERN_WARNING, "IOCTL_DISABLE_VE ve_moduleclk is invalid (%d)\n", __LINE__);
return -EFAULT;
} else {
clk_disable_unprepare(cedar_devp->mod_clk);
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if ((NULL == ve_moduleclk)||IS_ERR(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, "IOCTL_DISABLE_VE "
"ve_moduleclk is invalid\n");
return -EFAULT;
} else {
clk_disable_unprepare(ve_moduleclk);
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
break;
case IOCTL_RESET_VE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
reset_control_assert(cedar_devp->rstc);
reset_control_deassert(cedar_devp->rstc);
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
sunxi_periph_reset_assert(ve_moduleclk);
sunxi_periph_reset_deassert(ve_moduleclk);
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
break;
case IOCTL_SET_VE_FREQ:
{
int arg_rate = (int)arg;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
struct clk *const ve_moduleclk=cedar_devp->mod_clk;
struct clk *const ve_parent_pll_clk=cedar_devp->ahb_clk;
u32 ve_parent_clk_rate;
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if (arg_rate >= VE_CLK_LOW_WATER &&
arg_rate <= VE_CLK_HIGH_WATER &&
clk_get_rate(ve_moduleclk)/1000000 != arg_rate) {
if (!clk_set_rate(ve_parent_pll_clk, arg_rate*1000000)) {
ve_parent_clk_rate = clk_get_rate(ve_parent_pll_clk);
if (clk_set_rate(ve_moduleclk, ve_parent_clk_rate)) {
cedar_ve_printk(KERN_WARNING, "set ve clock failed\n");
}
} else {
cedar_ve_printk(KERN_WARNING, "set pll4 clock failed\n");
}
}
ret = clk_get_rate(ve_moduleclk);
break;
}
case IOCTL_GETVALUE_AVS2:
case IOCTL_ADJUST_AVS2:
case IOCTL_ADJUST_AVS2_ABS:
case IOCTL_CONFIG_AVS2:
case IOCTL_RESET_AVS2:
case IOCTL_PAUSE_AVS2:
case IOCTL_START_AVS2:
cedar_ve_printk(KERN_WARNING, "do not supprot this ioctrl now\n");
break;
case IOCTL_GET_ENV_INFO:
{
struct cedarv_env_infomation_compat env_info;
#if defined(USE_ION)
env_info.phymem_start = 0; // do not use this interface ,ve get phy mem form ion now
env_info.phymem_total_size = 0;//ve_size = 0x04000000
env_info.address_macc = 0;
#else
env_info.phymem_start = (unsigned long)phys_to_virt(cedar_devp->ve_start); // do not use this interface ,ve get phy mem form ion now
env_info.phymem_total_size = (unsigned long) cedar_devp->ve_size;
env_info.address_macc = (unsigned long) cedar_devp->iomap_addrs.regs_macc;
#endif
if (copy_to_user((char *)arg, &env_info,
sizeof(struct cedarv_env_infomation_compat)))
return -EFAULT;
}
break;
case IOCTL_GET_IC_VER:
{
return 0;
}
case IOCTL_FLUSH_CACHE:
#if !defined(MMAP_UNCACHABLE)
{
struct cedarv_cache_range cache_range;
if(copy_from_user(&cache_range, (void __user*)arg, sizeof(struct cedarv_cache_range))){
printk("IOCTL_FLUSH_CACHE copy_from_user fail\n");
return -EFAULT;
}
#ifndef __aarch64__
flush_clean_user_range(cache_range.start, cache_range.end);
#endif
}
#endif
break;
case IOCTL_SET_REFCOUNT:
cedar_devp->ref_count = (int)arg;
break;
case IOCTL_SET_VOL:
{
#if defined CONFIG_ARCH_SUN9IW1P1
int ret;
int vol = (int)arg;
if (down_interruptible(&cedar_devp->sem)) {
return -ERESTARTSYS;
}
info->set_vol_flag = 1;
//set output voltage to arg mV
ret = regulator_set_voltage(regu,vol*1000,3300000);
if (IS_ERR(regu)) {
cedar_ve_printk(KERN_WARNING, \
"fail to set axp15_dcdc4 regulator voltage!\n");
}
up(&cedar_devp->sem);
#endif
break;
}
default:
return -1;
}
return ret;
}
#endif /* CONFIG_COMPAT */
/*
* ioctl function
* including : wait video engine done,
* AVS Counter control,
* Physical memory control,
* module clock/freq control.
* cedar engine
*/
static long cedardev_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{
int ret = 0;
int ve_timeout = 0;
//struct cedar_dev *devp;
#ifdef USE_CEDAR_ENGINE
int rel_taskid = 0;
struct __cedarv_task task_ret;
struct cedarv_engine_task *task_ptr = NULL;
#endif
ulong flags;
struct ve_info *info;
info = filp->private_data;
switch (cmd)
{
case IOCTL_ENGINE_REQ:
#ifdef USE_CEDAR_ENGINE
if (copy_from_user(&task_ret, (void __user *)arg, sizeof(struct __cedarv_task))) {
cedar_ve_printk(KERN_WARNING, \
"IOCTL_ENGINE_REQ copy_from_user fail\n");
return -EFAULT;
}
spin_lock_irqsave(&cedar_spin_lock, flags);
if (!list_empty(&run_task_list) && (task_ret.block_mode == CEDAR_NONBLOCK_TASK)) {
spin_unlock_irqrestore(&cedar_spin_lock, flags);
return CEDAR_RUN_LIST_NONULL;
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
task_ptr = kmalloc(sizeof(struct cedarv_engine_task), GFP_KERNEL);
if (!task_ptr) {
cedar_ve_printk(KERN_WARNING, "get mem for IOCTL_ENGINE_REQ\n");
return PTR_ERR(task_ptr);
}
task_ptr->task_handle = current;
task_ptr->t.ID = task_ret.ID;
task_ptr->t.timeout = jiffies + msecs_to_jiffies(1000*task_ret.timeout);//ms to jiffies
task_ptr->t.frametime = task_ret.frametime;
task_ptr->t.task_prio = task_ret.task_prio;
task_ptr->running = 0;
task_ptr->is_first_task = 0;
task_ptr->status = TASK_INIT;
cedardev_insert_task(task_ptr);
ret = enable_cedar_hw_clk();
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, \
"cedar clk enable somewhere error!\n");
return -EFAULT;
}
return task_ptr->is_first_task;
#else
cedar_devp->ref_count++;
if (1 == cedar_devp->ref_count)
enable_cedar_hw_clk();
break;
#endif
case IOCTL_ENGINE_REL:
#ifdef USE_CEDAR_ENGINE
rel_taskid = (int)arg;
ret = cedardev_del_task(rel_taskid);
#else
cedar_devp->ref_count--;
if (0 == cedar_devp->ref_count) {
ret = disable_cedar_hw_clk();
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "IOCTL_ENGINE_REL "
"clk disable error!\n");
return -EFAULT;
}
}
#endif
return ret;
case IOCTL_ENGINE_CHECK_DELAY:
{
struct cedarv_engine_task_info task_info;
if (copy_from_user(&task_info,
(void __user *)arg,
sizeof(struct cedarv_engine_task_info))) {
cedar_ve_printk(KERN_WARNING, \
"IOCTL_ENGINE_CHECK_DELAY copy_from_user fail\n");
return -EFAULT;
}
task_info.total_time = cedardev_check_delay(task_info.task_prio);
#ifdef CEDAR_DEBUG
printk("%s,%d,%d\n", __func__, __LINE__, task_info.total_time);
#endif
task_info.frametime = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (!list_empty(&run_task_list)) {
struct cedarv_engine_task *task_entry;
#ifdef CEDAR_DEBUG
printk("%s,%d\n",__func__,__LINE__);
#endif
task_entry = list_entry(run_task_list.next, struct cedarv_engine_task, list);
if (task_entry->running == 1)
task_info.frametime = task_entry->t.frametime;
#ifdef CEDAR_DEBUG
printk("%s,%d,%d\n",__func__,__LINE__,task_info.frametime);
#endif
}
spin_unlock_irqrestore(&cedar_spin_lock, flags);
if (copy_to_user((void *)arg, &task_info, sizeof(struct cedarv_engine_task_info))){
cedar_ve_printk(KERN_WARNING, \
"IOCTL_ENGINE_CHECK_DELAY copy_to_user fail\n");
return -EFAULT;
}
}
break;
case IOCTL_WAIT_VE_DE:
ve_timeout = (int)arg;
cedar_devp->de_irq_value = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (cedar_devp->de_irq_flag)
cedar_devp->de_irq_value = 1;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
//printk("[cedar-ve]: ioctl IOCTL_WAIT_VE_DE\n");
wait_event_interruptible_timeout(wait_ve, cedar_devp->de_irq_flag, ve_timeout*HZ);
cedar_devp->de_irq_flag = 0;
return cedar_devp->de_irq_value;
case IOCTL_WAIT_VE_EN:
ve_timeout = (int)arg;
cedar_devp->en_irq_value = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (cedar_devp->en_irq_flag)
cedar_devp->en_irq_value = 1;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
//printk("[cedar-ve]: ioctl IOCTL_WAIT_VE_EN\n");
wait_event_interruptible_timeout(wait_ve, cedar_devp->en_irq_flag, ve_timeout*HZ);
cedar_devp->en_irq_flag = 0;
return cedar_devp->en_irq_value;
#if ((defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN50I))
case IOCTL_WAIT_JPEG_DEC:
ve_timeout = (int)arg;
cedar_devp->jpeg_irq_value = 0;
spin_lock_irqsave(&cedar_spin_lock, flags);
if (cedar_devp->jpeg_irq_flag)
cedar_devp->jpeg_irq_value = 1;
spin_unlock_irqrestore(&cedar_spin_lock, flags);
wait_event_interruptible_timeout(wait_ve, cedar_devp->jpeg_irq_flag, ve_timeout*HZ);
cedar_devp->jpeg_irq_flag = 0;
return cedar_devp->jpeg_irq_value;
#endif
case IOCTL_ENABLE_VE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if (clk_prepare_enable(cedar_devp->mod_clk)) {
cedar_ve_printk(KERN_WARNING, \
"try to enable ve_moduleclk failed!\n");
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if (clk_prepare_enable(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, \
"try to enable ve_moduleclk failed!\n");
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
break;
case IOCTL_DISABLE_VE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if ((NULL == cedar_devp->mod_clk)||IS_ERR(cedar_devp->mod_clk)) {
cedar_ve_printk(KERN_WARNING, \
"ve_moduleclk is invalid,just return!\n");
return -EFAULT;
} else {
clk_disable_unprepare(cedar_devp->mod_clk);
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if ((NULL == ve_moduleclk)||IS_ERR(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, \
"ve_moduleclk is invalid,just return!\n");
return -EFAULT;
} else {
clk_disable_unprepare(ve_moduleclk);
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
break;
case IOCTL_RESET_VE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
reset_control_assert(cedar_devp->rstc);
reset_control_deassert(cedar_devp->rstc);
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
sunxi_periph_reset_assert(ve_moduleclk);
sunxi_periph_reset_deassert(ve_moduleclk);
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
break;
case IOCTL_SET_VE_FREQ:
{
int arg_rate = (int)arg;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
struct clk *const ve_moduleclk=cedar_devp->mod_clk;
struct clk *const ve_parent_pll_clk=cedar_devp->ahb_clk;
u32 ve_parent_clk_rate;
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if (arg_rate >= VE_CLK_LOW_WATER &&
arg_rate <= VE_CLK_HIGH_WATER &&
clk_get_rate(ve_moduleclk)/1000000 != arg_rate) {
if (!clk_set_rate(ve_parent_pll_clk, arg_rate*1000000)) {
ve_parent_clk_rate = clk_get_rate(ve_parent_pll_clk);
if (clk_set_rate(ve_moduleclk, ve_parent_clk_rate)) {
cedar_ve_printk(KERN_WARNING, "set ve clock failed\n");
}
} else {
cedar_ve_printk(KERN_WARNING, "set pll4 clock failed\n");
}
}
ret = clk_get_rate(ve_moduleclk);
break;
}
case IOCTL_GETVALUE_AVS2:
case IOCTL_ADJUST_AVS2:
case IOCTL_ADJUST_AVS2_ABS:
case IOCTL_CONFIG_AVS2:
case IOCTL_RESET_AVS2:
case IOCTL_PAUSE_AVS2:
case IOCTL_START_AVS2:
cedar_ve_printk(KERN_WARNING, "do not supprot this ioctrl now\n");
break;
case IOCTL_GET_ENV_INFO:
{
struct cedarv_env_infomation env_info;
#if defined(USE_ION)
env_info.phymem_start = 0; // do not use this interface ,ve get phy mem form ion now
env_info.phymem_total_size = 0;//ve_size = 0x04000000
env_info.address_macc = 0;
#else
env_info.phymem_start = (unsigned long)phys_to_virt(cedar_devp->ve_start); // do not use this interface ,ve get phy mem form ion now
env_info.phymem_total_size = (unsigned long) cedar_devp->ve_size;
env_info.address_macc = (unsigned long) cedar_devp->iomap_addrs.regs_macc;
#endif
if (copy_to_user((char *)arg, &env_info, sizeof(struct cedarv_env_infomation)))
return -EFAULT;
}
break;
case IOCTL_GET_IC_VER:
{
return 0;
}
case IOCTL_FLUSH_CACHE:
#if !defined(MMAP_UNCACHABLE)
{
struct cedarv_cache_range cache_range;
if(copy_from_user(&cache_range, (void __user*)arg, sizeof(struct cedarv_cache_range))){
printk("IOCTL_FLUSH_CACHE copy_from_user fail\n");
return -EFAULT;
}
#ifndef __aarch64__
flush_clean_user_range(cache_range.start, cache_range.end);
#endif
}
#endif
break;
case IOCTL_SET_REFCOUNT:
cedar_devp->ref_count = (int)arg;
break;
case IOCTL_SET_VOL:
{
#if defined CONFIG_ARCH_SUN9IW1P1
int ret;
int vol = (int)arg;
if (down_interruptible(&cedar_devp->sem)) {
return -ERESTARTSYS;
}
info->set_vol_flag = 1;
//set output voltage to arg mV
ret = regulator_set_voltage(regu,vol*1000,3300000);
if (IS_ERR(regu))
cedar_ve_printk(KERN_WARNING, "fail to set axp15_dcdc4 regulator voltage!\n");
up(&cedar_devp->sem);
#endif
break;
}
default:
return -1;
}
return ret;
}
static int cedardev_open(struct inode *inode, struct file *filp)
{
//struct cedar_dev *devp;
struct ve_info *info;
info = kmalloc(sizeof(struct ve_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->set_vol_flag = 0;
ve_file = filp;
//devp = container_of(inode->i_cdev, struct cedar_dev, cdev);
filp->private_data = info;
if (down_interruptible(&cedar_devp->sem)) {
return -ERESTARTSYS;
}
/* init other resource here */
if (0 == cedar_devp->ref_count) {
cedar_devp->de_irq_flag = 0;
cedar_devp->en_irq_flag = 0;
cedar_devp->jpeg_irq_flag = 0;
}
up(&cedar_devp->sem);
nonseekable_open(inode, filp);
return 0;
}
static int cedardev_release(struct inode *inode, struct file *filp)
{
//struct cedar_dev *devp;
struct ve_info *info;
//int ret = 0;
info = filp->private_data;
if (down_interruptible(&cedar_devp->sem)) {
return -ERESTARTSYS;
}
#if defined CONFIG_ARCH_SUN9IW1P1
if (info->set_vol_flag == 1) {
regulator_set_voltage(regu,900000,3300000);
if (IS_ERR(regu)) {
cedar_ve_printk(KERN_WARNING, \
"some error happen, fail to set axp15_dcdc4 regulator voltage!\n");
return -EINVAL;
}
}
#endif
/* release other resource here */
if (0 == cedar_devp->ref_count) {
cedar_devp->de_irq_flag = 1;
cedar_devp->en_irq_flag = 1;
cedar_devp->jpeg_irq_flag = 1;
}
up(&cedar_devp->sem);
kfree(info);
ve_file = NULL;
return 0;
}
static void cedardev_vma_open(struct vm_area_struct *vma)
{
}
static void cedardev_vma_close(struct vm_area_struct *vma)
{
}
static struct vm_operations_struct cedardev_remap_vm_ops = {
.open = cedardev_vma_open,
.close = cedardev_vma_close,
};
#if defined(USE_ION)
static int cedardev_mmap(struct file *filp, struct vm_area_struct *vma)
{
u32 temp_pfn;
if (vma->vm_end - vma->vm_start == 0)
{
cedar_ve_printk(KERN_WARNING, "vma->vm_end is equal vma->vm_start : %lx\n",\
vma->vm_start);
return 0;
}
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
{
cedar_ve_printk(KERN_WARNING, \
"the vma->vm_pgoff is %lx,it is large than the largest page number\n", vma->vm_pgoff);
return -EINVAL;
}
temp_pfn = MACC_REGS_BASE >> 12;
/* Set reserved and I/O flag for the area. */
vma->vm_flags |= /*VM_RESERVED | */VM_IO;
/* Select uncached access. */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, temp_pfn,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
return -EAGAIN;
}
vma->vm_ops = &cedardev_remap_vm_ops;
cedardev_vma_open(vma);
return 0;
}
#else
static int cedardev_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long temp_pfn;
unsigned long VAddr;
struct iomap_para addrs;
unsigned int io_ram = 0;
VAddr = vma->vm_pgoff << PAGE_SHIFT;
addrs = cedar_devp->iomap_addrs;
size_t size = vma->vm_end - vma->vm_start;
if (VAddr == (unsigned long)addrs.regs_macc) {
temp_pfn = MACC_REGS_BASE >> PAGE_SHIFT;
io_ram = 1;
} else {
temp_pfn = (__pa(vma->vm_pgoff << PAGE_SHIFT)) >> PAGE_SHIFT;
io_ram = 0;
}
if (io_ram == 0) {
/* Set reserved and I/O flag for the area. */
vma->vm_flags |= /* VM_RESERVED | */VM_IO;
#if defined(MMAP_UNCACHABLE)
/* Select uncached access. */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
# ifndef __aarch64__
if (remap_pfn_range(vma, vma->vm_start, temp_pfn,
size, vma->vm_page_prot)) {
return -EAGAIN;
}
# else
dma_mmap_coherent(cedar_devp->dev, vma, cedar_devp->ve_start_virt, cedar_devp->ve_start_pa, size);
# endif
#endif
} else {
/* Set reserved and I/O flag for the area. */
vma->vm_flags |= /*VM_RESERVED |*/ VM_IO;
/* Select uncached access. */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, temp_pfn,
size, vma->vm_page_prot)) {
return -EAGAIN;
}
}
vma->vm_ops = &cedardev_remap_vm_ops;
cedardev_vma_open(vma);
return 0;
}
#endif
#ifdef CONFIG_PM
static int snd_sw_cedar_suspend(struct platform_device *pdev,pm_message_t state)
{
int ret = 0;
printk("[cedar] standby suspend\n");
ret = disable_cedar_hw_clk();
#if defined CONFIG_ARCH_SUN9IW1P1
clk_disable_unprepare(ve_power_gating);
#endif
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "cedar clk disable somewhere error!\n");
return -EFAULT;
}
return 0;
}
static int snd_sw_cedar_resume(struct platform_device *pdev)
{
int ret = 0;
printk("[cedar] standby resume\n");
#if defined CONFIG_ARCH_SUN9IW1P1
clk_prepare_enable(ve_power_gating);
#endif
if (cedar_devp->ref_count == 0) {
return 0;
}
ret = enable_cedar_hw_clk();
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "cedar clk enable somewhere error!\n");
return -EFAULT;
}
return 0;
}
#endif
static const struct file_operations cedardev_fops = {
.owner = THIS_MODULE,
.mmap = cedardev_mmap,
.open = cedardev_open,
.release = cedardev_release,
.llseek = no_llseek,
.unlocked_ioctl = cedardev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_cedardev_ioctl,
#endif
};
static int cedardev_init(struct platform_device *pdev)
{
int ret = 0;
int devno;
#ifdef CONFIG_OF
struct device_node *node;
#endif /*CONFIG_OF*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
struct device_node *np;
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
dev_t dev;
dev = 0;
printk("[cedar]: install start!!!\n");
#if defined(CONFIG_OF)
node = pdev->dev.of_node;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
np = of_find_matching_node(NULL, sunxi_cedar_ve_match);
if (!np) {
printk(KERN_ERR "Couldn't find the VE node\n");
return -ENODEV;
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
/*register or alloc the device number.*/
if (g_dev_major) {
dev = MKDEV(g_dev_major, g_dev_minor);
ret = register_chrdev_region(dev, 1, "cedar_dev");
} else {
ret = alloc_chrdev_region(&dev, g_dev_minor, 1, "cedar_dev");
g_dev_major = MAJOR(dev);
g_dev_minor = MINOR(dev);
}
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "cedar_dev: can't get major %d\n", \
g_dev_major);
return ret;
}
spin_lock_init(&cedar_spin_lock);
cedar_devp = kmalloc(sizeof(struct cedar_dev), GFP_KERNEL);
if (cedar_devp == NULL) {
cedar_ve_printk(KERN_WARNING, "malloc mem for cedar device err\n");
return -ENOMEM;
}
memset(cedar_devp, 0, sizeof(struct cedar_dev));
cedar_devp->dev = &pdev->dev;
cedar_devp->pdev = pdev;
#if defined(CONFIG_OF)
cedar_devp->irq = irq_of_parse_and_map(node, 0);
cedar_ve_printk(KERN_INFO, "cedar-ve the get irq is %d\n", \
cedar_devp->irq);
if (cedar_devp->irq <= 0)
cedar_ve_printk(KERN_WARNING, "Can't parse IRQ");
#else
cedar_devp->irq = SUNXI_IRQ_VE;
#endif
#if defined(CONFIG_OF) && !defined(USE_ION)
// assign reserved memory for the VE
ret = of_reserved_mem_device_init(cedar_devp->dev);
if (ret) {
dev_err(cedar_devp->dev, "could not assign reserved memory\n");
return -ENODEV;
}
#endif
sema_init(&cedar_devp->sem, 1);
init_waitqueue_head(&cedar_devp->wq);
memset(&cedar_devp->iomap_addrs, 0, sizeof(struct iomap_para));
ret = request_irq(cedar_devp->irq, VideoEngineInterupt, 0, "cedar_dev", NULL);
if (ret < 0) {
cedar_ve_printk(KERN_WARNING, "request irq err\n");
return -EINVAL;
}
/* map for macc io space */
#if defined(CONFIG_OF)
cedar_devp->iomap_addrs.regs_macc = of_iomap(node, 0);
if (!cedar_devp->iomap_addrs.regs_macc)
cedar_ve_printk(KERN_WARNING, "ve Can't map registers");
cedar_devp->sram_bass_vir = (u32*)of_iomap(node, 1);
if (!cedar_devp->sram_bass_vir)
cedar_ve_printk(KERN_WARNING, "ve Can't map sram_bass_vir registers");
cedar_devp->clk_bass_vir = (u32*)of_iomap(node, 2);
if (!cedar_devp->clk_bass_vir)
cedar_ve_printk(KERN_WARNING, "ve Can't map clk_bass_vir registers");
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0) && ((defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN8IW9P1))
#ifdef CONFIG_OF
if (of_device_is_compatible(np, "allwinner,sun8i-h3-video-engine")) {
#endif /* CONFIG_OF*/
/*VE_SRAM mapping to AC320*/
{
u32 val;
//printk(KERN_DEBUG "patching H3... %08x\n"\n, sunxi_smc_readl((void __iomem *)0xf1c00000));
val = sunxi_smc_readl((void __iomem *)0xf1c00000);
val &= 0x80000000;
sunxi_smc_writel(val, (void __iomem *)0xf1c00000);
//printk(KERN_DEBUG "patching H3... %08x\n"\n, sunxi_smc_readl((void __iomem *)0xf1c00000));
/*remapping SRAM to MACC for codec test*/
val = sunxi_smc_readl((void __iomem *)0xf1c00000);
val |= 0x7fffffff;
sunxi_smc_writel(val, (void __iomem *)0xf1c00000);
//clear bootmode bit for give sram to ve
val = sunxi_smc_readl((void __iomem *)0xf1c00004);
val &= 0xfeffffff;
sunxi_smc_writel(val, (void __iomem *)0xf1c00004);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
ve_parent_pll_clk = clk_get(NULL, "pll_ve");
if ((!ve_parent_pll_clk)||IS_ERR(ve_parent_pll_clk)) {
printk("try to get ve_parent_pll_clk fail\n");
return -EINVAL;
}
#endif /*LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)*/
#ifdef CONFIG_OF
} else {
#endif /* CONFIG_OF*/
#if (defined CONFIG_ARCH_SUNIVW1P1) /*for 1663*/
{
u32 val;
val = readl(cedar_devp->clk_bass_vir+6);
val &= 0x7fff80f0;
val = val | (1<<31) | (8<<8);
writel(val, cedar_devp->clk_bass_vir+6);
/*set VE clock dividor*/
val = readl(cedar_devp->clk_bass_vir+79);
val |= (1<<31);
writel(val, cedar_devp->clk_bass_vir+79);
/*Active AHB bus to MACC*/
val = readl(cedar_devp->clk_bass_vir+25);
val |= (1<<0);
writel(val, cedar_devp->clk_bass_vir+25);
/*Power on and release reset ve*/
val = readl(cedar_devp->clk_bass_vir+177);
val &= ~(1<<0); /*reset ve*/
writel(val, cedar_devp->clk_bass_vir+177);
val = readl(cedar_devp->clk_bass_vir+177);
val |= (1<<0);
writel(val, cedar_devp->clk_bass_vir+177);
/*gate on the bus to SDRAM*/
val = readl(cedar_devp->clk_bass_vir+64);
val |= (1<<0);
writel(val, cedar_devp->clk_bass_vir+64);
/*VE_SRAM mapping to AC320*/
val = readl(cedar_devp->sram_bass_vir);
val &= 0x80000000;
writel(val, cedar_devp->sram_bass_vir);
/*remapping SRAM to MACC for codec test*/
val = readl(cedar_devp->sram_bass_vir);
val |= 0x7fffffff;
writel(val, cedar_devp->sram_bass_vir);
/*clear bootmode bit for give sram to ve*/
val = readl((cedar_devp->sram_bass_vir + 1));
val &= 0xefffffff;
writel(val, (cedar_devp->sram_bass_vir + 1));
}
#else
{
u32 val;
/*VE_SRAM mapping to AC320*/
val = readl(cedar_devp->sram_bass_vir);
val &= 0x80000000;
writel(val, cedar_devp->sram_bass_vir);
/*remapping SRAM to MACC for codec test*/
val = readl(cedar_devp->sram_bass_vir);
val |= 0x7fffffff;
writel(val, cedar_devp->sram_bass_vir);
/*clear bootmode bit for give sram to ve*/
val = readl((cedar_devp->sram_bass_vir + 1));
val &= 0xfeffffff;
writel(val, (cedar_devp->sram_bass_vir + 1));
}
#endif
#ifdef CONFIG_OF
}
#endif /* CONFIG_OF*/
#endif /* ((defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW8P1) || (defined CONFIG_ARCH_SUN8IW9P1)) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
cedar_devp->syscon = syscon_regmap_lookup_by_phandle(cedar_devp->dev->of_node, "syscon");
if (IS_ERR(cedar_devp->syscon)) {
dev_err(cedar_devp->dev, "syscon failed...\n");
cedar_devp->syscon = NULL;
} else {
// remap SRAM C1 to the VE
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,1,0)
int ret = sunxi_sram_claim(cedar_devp->dev);
if (ret) {
dev_err(cedar_devp->dev, "Failed to claim SRAM\n");
return -EFAULT;
}
printk("[cedar]: Success claim SRAM\n");
#else
regmap_write_bits(cedar_devp->syscon, SYSCON_SRAM_CTRL_REG0,
SYSCON_SRAM_C1_MAP_VE,
SYSCON_SRAM_C1_MAP_VE);
#endif
}
cedar_devp->ahb_clk = devm_clk_get(cedar_devp->dev, "ahb");
if (IS_ERR(cedar_devp->ahb_clk)) {
dev_err(cedar_devp->dev, "failed to get ahb clock\n");
return PTR_ERR(cedar_devp->ahb_clk);
}
cedar_devp->mod_clk = devm_clk_get(cedar_devp->dev, "mod");
if (IS_ERR(cedar_devp->mod_clk)) {
dev_err(cedar_devp->dev, "failed to get mod clock\n");
return PTR_ERR(cedar_devp->mod_clk);
}
cedar_devp->ram_clk = devm_clk_get(cedar_devp->dev, "ram");
if (IS_ERR(cedar_devp->ram_clk)) {
dev_err(cedar_devp->dev, "failed to get ram clock\n");
return PTR_ERR(cedar_devp->ram_clk);
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
# if defined(CONFIG_OF)
ve_parent_pll_clk = of_clk_get(node, 0);
if ((!ve_parent_pll_clk) || IS_ERR(ve_parent_pll_clk)) {
cedar_ve_printk(KERN_WARNING, "try to get ve_parent_pll_clk fail\n");
return -EINVAL;
}
ve_moduleclk = of_clk_get(node, 1);
if (!ve_moduleclk || IS_ERR(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, "get ve_moduleclk failed; \n");
}
# endif
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
// no reset ve module
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
ret = clk_prepare_enable(cedar_devp->ahb_clk);
if (ret) {
dev_err(cedar_devp->dev, "could not enable ahb clock\n");
return -EFAULT;
}
ret = clk_prepare_enable(cedar_devp->mod_clk);
if (ret) {
clk_disable_unprepare(cedar_devp->ahb_clk);
dev_err(cedar_devp->dev, "could not enable mod clock\n");
return -EFAULT;
}
ret = clk_prepare_enable(cedar_devp->ram_clk);
if (ret) {
clk_disable_unprepare(cedar_devp->mod_clk);
clk_disable_unprepare(cedar_devp->ahb_clk);
dev_err(cedar_devp->dev, "could not enable ram clock\n");
return -EFAULT;
}
cedar_devp->rstc = devm_reset_control_get(cedar_devp->dev, NULL);
if (IS_ERR(cedar_devp->rstc)) {
dev_err(cedar_devp->dev, "Failed to get reset control\n");
ret = PTR_ERR(cedar_devp->rstc);
return -EFAULT;
}
reset_control_assert(cedar_devp->rstc);
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
sunxi_periph_reset_assert(ve_moduleclk);
clk_prepare(ve_moduleclk);
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
/* Create char device */
devno = MKDEV(g_dev_major, g_dev_minor);
cdev_init(&cedar_devp->cdev, &cedardev_fops);
cedar_devp->cdev.owner = THIS_MODULE;
//cedar_devp->cdev.ops = &cedardev_fops;
ret = cdev_add(&cedar_devp->cdev, devno, 1);
if (ret) {
cedar_ve_printk(KERN_WARNING, "Err:%d add cedardev", ret);
}
cedar_devp->class = class_create(THIS_MODULE, "cedar_dev");
cedar_devp->odev = device_create(cedar_devp->class, NULL, devno, NULL, "cedar_dev");
#ifdef HAVE_TIMER_SETUP
timer_setup(&cedar_devp->cedar_engine_timer, &cedar_engine_for_events, 0);
timer_setup(&cedar_devp->cedar_engine_timer_rel, &cedar_engine_for_timer_rel, 0);
#else
setup_timer(&cedar_devp->cedar_engine_timer, cedar_engine_for_events, (ulong)cedar_devp);
setup_timer(&cedar_devp->cedar_engine_timer_rel, cedar_engine_for_timer_rel, (ulong)cedar_devp);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
of_node_put(np);
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
#if !defined(USE_ION)
cedar_devp->ve_size = 80 * SZ_1M;
ret = dma_set_coherent_mask(cedar_devp->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(cedar_devp->dev, "DMA enable failed\n");
return ret;
}
cedar_devp->ve_start_virt = dma_alloc_coherent(cedar_devp->dev, cedar_devp->ve_size,
&cedar_devp->ve_start_pa,
GFP_KERNEL | GFP_DMA);
if (!cedar_devp->ve_start_virt) {
dev_err(cedar_devp->dev, "cedar: failed to allocate memory buffer\n");
return -ENODEV;
}
cedar_devp->ve_start = cedar_devp->ve_start_pa;
#ifndef __aarch64__
printk("[cedar]: memory allocated at address PA: %08lX, VA: %08lX\n",
cedar_devp->ve_start, (ulong)cedar_devp->ve_start_virt);
#else
printk("[cedar]: memory allocated at PA: %016lX, VA: %016lX, CONV: %016lX\n",
cedar_devp->ve_start,
(ulong)cedar_devp->ve_start_virt,
(ulong) phys_to_virt(cedar_devp->ve_start));
printk("[cedar]: MACC regs allocated at %016lX\n", (ulong)cedar_devp->iomap_addrs.regs_macc);
printk("PAGE_OFFSET = %16lx\n", PAGE_OFFSET);
printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);
printk("PAGE_MASK = %16lx\n", PAGE_MASK);
printk("PHYS_OFFSET = %16lx\n", PHYS_OFFSET);
#endif
#endif
printk("[cedar]: install end!!!\n");
return 0;
}
static void cedardev_exit(void)
{
dev_t dev;
dev = MKDEV(g_dev_major, g_dev_minor);
free_irq(cedar_devp->irq, NULL);
iounmap(cedar_devp->iomap_addrs.regs_macc);
// iounmap(cedar_devp->iomap_addrs.regs_avs);
if (cedar_devp->sram_bass_vir) iounmap(cedar_devp->sram_bass_vir);
if (cedar_devp->clk_bass_vir) iounmap(cedar_devp->clk_bass_vir);
#if !defined(USE_ION)
if (cedar_devp->ve_start_virt)
dma_free_coherent(cedar_devp->dev, cedar_devp->ve_size, cedar_devp->ve_start_virt, cedar_devp->ve_start_pa);
# if defined(CONFIG_OF)
of_reserved_mem_device_release(cedar_devp->dev);
# endif
#endif
/* Destroy char device */
if (cedar_devp) {
cdev_del(&cedar_devp->cdev);
device_destroy(cedar_devp->class, dev);
class_destroy(cedar_devp->class);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if (NULL == cedar_devp->mod_clk || IS_ERR(cedar_devp->mod_clk)) { cedar_ve_printk(KERN_WARNING, "ve_moduleclk handle is invalid,just return! (%d)\n", __LINE__); }
if (NULL == cedar_devp->ram_clk || IS_ERR(cedar_devp->ram_clk)) { cedar_ve_printk(KERN_WARNING, "ve_moduleclk handle is invalid,just return! (%d)\n", __LINE__); }
clk_disable_unprepare(cedar_devp->mod_clk);
clk_disable_unprepare(cedar_devp->ram_clk);
cedar_devp->mod_clk = NULL;
cedar_devp->ram_clk = NULL;
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if (NULL == ve_moduleclk || IS_ERR(ve_moduleclk)) {
cedar_ve_printk(KERN_WARNING, "ve_moduleclk handle "
"is invalid,just return!\n");
} else {
clk_disable_unprepare(ve_moduleclk);
clk_put(ve_moduleclk);
ve_moduleclk = NULL;
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
if (NULL == cedar_devp->ahb_clk || IS_ERR(cedar_devp->ahb_clk)) {
cedar_ve_printk(KERN_WARNING, "ve_parent_pll_clk handle is invalid,just return!\n");
} else {
clk_disable_unprepare(cedar_devp->ahb_clk);
cedar_devp->ahb_clk = NULL;
}
#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
if (NULL == ve_parent_pll_clk || IS_ERR(ve_parent_pll_clk)) {
cedar_ve_printk(KERN_WARNING, "ve_parent_pll_clk "
"handle is invalid,just return!\n");
} else {
clk_put(ve_parent_pll_clk);
}
#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)*/
#if defined CONFIG_ARCH_SUN9IW1P1
//put regulator when module exit
regulator_put(regu);
if (NULL == ve_power_gating || IS_ERR(ve_power_gating)) {
cedar_ve_printk(KERN_WARNING, "ve_power_gating "
"handle is invalid,just return!\n");
} else {
clk_disable_unprepare(ve_power_gating);
clk_put(ve_power_gating);
ve_power_gating = NULL;
}
#endif
unregister_chrdev_region(dev, 1);
// platform_driver_unregister(&sw_cedar_driver);
if (cedar_devp) {
kfree(cedar_devp);
}
}
static int sunxi_cedar_remove(struct platform_device *pdev)
{
cedardev_exit();
return 0;
}
static int sunxi_cedar_probe(struct platform_device *pdev)
{
cedardev_init(pdev);
return 0;
}
/*share the irq no. with timer2*/
/*
static struct resource sunxi_cedar_resource[] = {
[0] = {
.start = SUNXI_IRQ_VE,
.end = SUNXI_IRQ_VE,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device sunxi_device_cedar = {
.name = "sunxi-cedar",
.id = -1,
.num_resources = ARRAY_SIZE(sunxi_cedar_resource),
.resource = sunxi_cedar_resource,
};
*/
static struct platform_driver sunxi_cedar_driver = {
.probe = sunxi_cedar_probe,
.remove = sunxi_cedar_remove,
#ifdef CONFIG_PM
.suspend = snd_sw_cedar_suspend,
.resume = snd_sw_cedar_resume,
#endif
.driver = {
.name = "sunxi-cedar",
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = sunxi_cedar_ve_match,
#endif
},
};
static int __init sunxi_cedar_init(void)
{
//need not to gegister device here,because the device is registered by device tree
//platform_device_register(&sunxi_device_cedar);
printk("sunxi cedar version 0.1 \n");
return platform_driver_register(&sunxi_cedar_driver);
}
static void __exit sunxi_cedar_exit(void)
{
platform_driver_unregister(&sunxi_cedar_driver);
}
module_init(sunxi_cedar_init);
module_exit(sunxi_cedar_exit);
MODULE_AUTHOR("Soft-Reuuimlla");
MODULE_DESCRIPTION("User mode CEDAR device interface");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_ALIAS("platform:cedarx-sunxi");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment