1023 lines
26 KiB
C
1023 lines
26 KiB
C
/*
|
|
* drivers\media\google_vp9
|
|
* (C) Copyright 2010-2016
|
|
* Reuuimlla Technology Co., Ltd. <www.allwinnertech.com>
|
|
* yangcaoyuan<yangcaoyuan@allwinnertech.com>
|
|
*
|
|
* some simple description for this code
|
|
*
|
|
* 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/scatterlist.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/io.h>
|
|
#include <asm/dma.h>
|
|
#include <linux/mm.h>
|
|
#include <asm/siginfo.h>
|
|
#include <asm/signal.h>
|
|
#include <linux/clk/sunxi.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
|
|
#include "google_vp9.h"
|
|
#include "vp9_mem_list.h"
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dma-buf.h>
|
|
|
|
#define DRV_VERSION "0.01alpha"
|
|
|
|
#ifndef VP9DEV_MAJOR
|
|
#define VP9DEV_MAJOR (0)
|
|
#endif
|
|
#ifndef VP9DEV_MINOR
|
|
#define VP9DEV_MINOR (0)
|
|
#endif
|
|
|
|
#define MACC_VP9_REGS_BASE (0x01C00000)
|
|
|
|
#ifndef CONFIG_OF
|
|
#define SUNXI_IRQ_GOOGLE_VP9 (122)
|
|
#endif
|
|
|
|
/*#define VP9_DEBUG*/
|
|
#define google_vp9_printk(level, msg...) printk(level "google_vp9: " msg)
|
|
|
|
#define GOOGLE_VP9_CLK_HIGH_WATER (700)
|
|
#define GOOGLE_VP9_CLK_LOW_WATER (100)
|
|
|
|
#define PRINTK_IOMMU_ADDR 0
|
|
|
|
int vp9_dev_major = VP9DEV_MAJOR;
|
|
int vp9_dev_minor = VP9DEV_MINOR;
|
|
module_param(vp9_dev_major, int, 0444);
|
|
module_param(vp9_dev_minor, int, 0444);
|
|
|
|
struct clk *google_vp9_moduleclk;
|
|
struct clk *google_vp9_parent_pll_clk;
|
|
|
|
static unsigned long google_vp9_parent_clk_rate = 300000000;
|
|
|
|
struct iomap_para {
|
|
char *regs_macc;
|
|
char *regs_avs;
|
|
};
|
|
|
|
struct user_iommu_param {
|
|
int fd;
|
|
unsigned int iommu_addr;
|
|
};
|
|
|
|
struct cedarv_iommu_buffer {
|
|
struct aw_mem_list_head i_list;
|
|
int fd;
|
|
unsigned long iommu_addr;
|
|
struct dma_buf *dma_buf;
|
|
struct dma_buf_attachment *attachment;
|
|
struct sg_table *sgt;
|
|
int p_id;
|
|
};
|
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(wait_vp9);
|
|
struct googlevp9_dev {
|
|
struct cdev cdev; /* char device struct */
|
|
struct device *dev; /* ptr to class device struct */
|
|
struct device *platform_dev; /* ptr to class device struct */
|
|
struct class *class; /* class for auto create device node */
|
|
|
|
struct semaphore sem; /* mutual exclusion semaphore */
|
|
|
|
wait_queue_head_t wq; /* wait queue for poll ops */
|
|
|
|
struct iomap_para iomap_addrs; /* io remap addrs */
|
|
|
|
struct timer_list vp9_engine_timer;
|
|
struct timer_list vp9_engine_timer_rel;
|
|
|
|
u32 irq; /* cedar video engine irq number */
|
|
u32 de_irq_flag; /* flag of video decoder engine irq generated */
|
|
u32 de_irq_value; /* value of video decoder engine irq */
|
|
u32 irq_has_enable;
|
|
u32 ref_count;
|
|
|
|
unsigned int *sram_bass_vir;
|
|
unsigned int *clk_bass_vir;
|
|
|
|
struct aw_mem_list_head list; /* buffer list */
|
|
struct mutex lock_mem;
|
|
unsigned char bMemDevAttachFlag;
|
|
};
|
|
|
|
struct google_vp9_info {
|
|
unsigned int set_vol_flag;
|
|
};
|
|
|
|
struct googlevp9_dev *google_vp9_devp;
|
|
struct file *google_vp9_file;
|
|
|
|
#if defined(CONFIG_OF)
|
|
static const struct of_device_id sunxi_google_vp9_match[] = {
|
|
{ .compatible = "allwinner,sunxi-google-vp9",},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, sunxi_google_vp9_match);
|
|
#endif
|
|
|
|
static irqreturn_t GoogleVp9Interrupt(int irq, void *dev)
|
|
{
|
|
unsigned long vp9_int_status_reg;
|
|
unsigned long vp9_int_ctrl_reg;
|
|
unsigned int status;
|
|
unsigned int interrupt_enable;
|
|
struct iomap_para addrs = google_vp9_devp->iomap_addrs;
|
|
/*1. check and get the interrupt enable bits */
|
|
/*2. check and get the interrupt status bits */
|
|
/*3. clear the interrupt enable bits */
|
|
/*4. set the irq_value and irq_flag */
|
|
/*5. wake up the user mode interrupt_func */
|
|
vp9_int_status_reg = (unsigned long)(addrs.regs_macc + 0x04);
|
|
status = readl((void *)vp9_int_status_reg);
|
|
vp9_int_ctrl_reg = (unsigned long)(addrs.regs_macc + 0x04);
|
|
interrupt_enable = (readl((void *)vp9_int_ctrl_reg) & (0x10));
|
|
|
|
/* only check status[bit:18,16,13,12,11,8], enable[bit:4] */
|
|
if ((status & 0x53900) && (!interrupt_enable)) {
|
|
/*need check we must clear the interrupt enable bits or not?*/
|
|
unsigned int val;
|
|
|
|
val = readl((void *)vp9_int_ctrl_reg);
|
|
writel(val & 0xfffffeff, (void *)vp9_int_ctrl_reg);
|
|
google_vp9_devp->de_irq_value = 1;
|
|
google_vp9_devp->de_irq_flag = 1;
|
|
wake_up_interruptible(&wait_vp9);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int clk_status;
|
|
static LIST_HEAD(run_task_list);
|
|
static LIST_HEAD(del_task_list);
|
|
static spinlock_t google_vp9_spin_lock;
|
|
#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
|
|
#define SIG_CEDAR 35
|
|
|
|
int enable_google_vp9_hw_clk(void)
|
|
{
|
|
unsigned long flags;
|
|
int res = -EFAULT;
|
|
|
|
spin_lock_irqsave(&google_vp9_spin_lock, flags);
|
|
|
|
if (clk_status == 1)
|
|
goto out;
|
|
|
|
clk_status = 1;
|
|
|
|
sunxi_periph_reset_deassert(google_vp9_moduleclk);
|
|
if (clk_enable(google_vp9_moduleclk)) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"enable google_vp9_moduleclk failed;\n");
|
|
goto out;
|
|
} else {
|
|
res = 0;
|
|
}
|
|
|
|
AW_MEM_INIT_LIST_HEAD(&google_vp9_devp->list);
|
|
out:
|
|
spin_unlock_irqrestore(&google_vp9_spin_lock, flags);
|
|
return res;
|
|
}
|
|
|
|
int disable_google_vp9_hw_clk(void)
|
|
{
|
|
unsigned long flags;
|
|
int res = -EFAULT;
|
|
struct aw_mem_list_head *pos, *q;
|
|
|
|
spin_lock_irqsave(&google_vp9_spin_lock, flags);
|
|
|
|
if (clk_status == 0) {
|
|
res = 0;
|
|
goto out;
|
|
}
|
|
clk_status = 0;
|
|
|
|
if ((google_vp9_moduleclk == NULL) ||
|
|
(IS_ERR(google_vp9_moduleclk))) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"google_vp9_moduleclk is invalid\n");
|
|
} else {
|
|
clk_disable(google_vp9_moduleclk);
|
|
sunxi_periph_reset_assert(google_vp9_moduleclk);
|
|
res = 0;
|
|
}
|
|
|
|
aw_mem_list_for_each_safe(pos, q, &google_vp9_devp->list) {
|
|
struct cedarv_iommu_buffer *tmp;
|
|
|
|
tmp = aw_mem_list_entry(pos, struct cedarv_iommu_buffer, i_list);
|
|
aw_mem_list_del(pos);
|
|
kfree(tmp);
|
|
}
|
|
out:
|
|
spin_unlock_irqrestore(&google_vp9_spin_lock, flags);
|
|
return res;
|
|
}
|
|
|
|
static void vp9_engine_for_timer_rel(unsigned long arg)
|
|
{
|
|
unsigned long flags;
|
|
int ret = 0;
|
|
|
|
spin_lock_irqsave(&google_vp9_spin_lock, flags);
|
|
|
|
if (list_empty(&run_task_list)) {
|
|
ret = disable_google_vp9_hw_clk();
|
|
if (ret < 0)
|
|
google_vp9_printk(KERN_WARNING, "clk disable error!\n");
|
|
} else {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"clk disable time out but task left\n");
|
|
mod_timer(&google_vp9_devp->vp9_engine_timer,
|
|
jiffies + msecs_to_jiffies(TIMER_CIRCLE));
|
|
}
|
|
|
|
spin_unlock_irqrestore(&google_vp9_spin_lock, flags);
|
|
}
|
|
|
|
static void vp9_engine_for_events(unsigned long arg)
|
|
{
|
|
struct vp9_engine_task *task_entry, *task_entry_tmp;
|
|
struct siginfo info;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&google_vp9_spin_lock, flags);
|
|
|
|
list_for_each_entry_safe(task_entry,
|
|
task_entry_tmp,
|
|
&run_task_list, list) {
|
|
mod_timer(&google_vp9_devp->vp9_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 vp9_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(&google_vp9_devp->vp9_engine_timer,
|
|
jiffies + msecs_to_jiffies(TIMER_CIRCLE));
|
|
}
|
|
|
|
spin_unlock_irqrestore(&google_vp9_spin_lock, flags);
|
|
}
|
|
|
|
static long compat_googlevp9dev_ioctl(struct file *filp,
|
|
unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
long ret = 0;
|
|
int ve_timeout = 0;
|
|
unsigned long flags;
|
|
struct google_vp9_info *info;
|
|
|
|
info = filp->private_data;
|
|
|
|
switch (cmd) {
|
|
case VP9_IOCTL_ENGINE_REQ:
|
|
google_vp9_devp->ref_count++;
|
|
if (google_vp9_devp->ref_count == 1)
|
|
enable_google_vp9_hw_clk();
|
|
break;
|
|
case VP9_IOCTL_ENGINE_REL:
|
|
google_vp9_devp->ref_count--;
|
|
if (google_vp9_devp->ref_count == 0) {
|
|
ret = disable_google_vp9_hw_clk();
|
|
if (ret < 0) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"IOCTL_ENGINE_REL clk disable error!\n");
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
return ret;
|
|
case VP9_IOCTL_WAIT_INTERRUPT:
|
|
ve_timeout = (int)arg;
|
|
google_vp9_devp->de_irq_value = 0;
|
|
|
|
spin_lock_irqsave(&google_vp9_spin_lock, flags);
|
|
if (google_vp9_devp->de_irq_flag)
|
|
google_vp9_devp->de_irq_value = 1;
|
|
spin_unlock_irqrestore(&google_vp9_spin_lock, flags);
|
|
|
|
wait_event_interruptible_timeout(wait_vp9,
|
|
google_vp9_devp->de_irq_flag, ve_timeout*HZ);
|
|
google_vp9_devp->de_irq_flag = 0;
|
|
|
|
return google_vp9_devp->de_irq_value;
|
|
|
|
case VP9_IOCTL_RESET:
|
|
sunxi_periph_reset_assert(google_vp9_moduleclk);
|
|
sunxi_periph_reset_deassert(google_vp9_moduleclk);
|
|
break;
|
|
|
|
case VP9_IOCTL_SET_FREQ:
|
|
{
|
|
int arg_rate = (int)arg;
|
|
|
|
if (arg_rate >= GOOGLE_VP9_CLK_LOW_WATER &&
|
|
arg_rate <= GOOGLE_VP9_CLK_HIGH_WATER &&
|
|
clk_get_rate(google_vp9_moduleclk)/1000000 != arg_rate) {
|
|
if (!clk_set_rate(google_vp9_parent_pll_clk, arg_rate*1000000)) {
|
|
google_vp9_parent_clk_rate = clk_get_rate(google_vp9_parent_pll_clk);
|
|
|
|
if (clk_set_rate(google_vp9_moduleclk,
|
|
google_vp9_parent_clk_rate))
|
|
google_vp9_printk(KERN_WARNING,
|
|
"set vp9 clock failed\n");
|
|
} else {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"set pll_vp9_parent clock failed\n");
|
|
}
|
|
}
|
|
ret = clk_get_rate(google_vp9_moduleclk);
|
|
break;
|
|
}
|
|
|
|
case VP9_IOCTL_GET_ENV_INFO:
|
|
{
|
|
struct vp9_env_information_compat env_info;
|
|
|
|
env_info.phymem_start = 0;
|
|
env_info.phymem_total_size = 0;
|
|
env_info.address_macc = 0;
|
|
if (copy_to_user((char *)arg, &env_info,
|
|
sizeof(struct vp9_env_information_compat)))
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_GET_IOMMU_ADDR:
|
|
{
|
|
int ret, i;
|
|
struct sg_table *sgt, *sgt_bak;
|
|
struct scatterlist *sgl, *sgl_bak;
|
|
struct user_iommu_param sUserIommuParam;
|
|
struct cedarv_iommu_buffer *pVeIommuBuf = NULL;
|
|
|
|
google_vp9_devp->bMemDevAttachFlag = 1;
|
|
|
|
pVeIommuBuf = kmalloc(sizeof(struct cedarv_iommu_buffer), GFP_KERNEL);
|
|
if (pVeIommuBuf == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(&sUserIommuParam, (void __user *)arg,
|
|
sizeof(struct user_iommu_param))) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"IOCTL_GET_IOMMU_ADDR copy_from_user erro\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
pVeIommuBuf->fd = sUserIommuParam.fd;
|
|
|
|
pVeIommuBuf->dma_buf = dma_buf_get(pVeIommuBuf->fd);
|
|
if (pVeIommuBuf->dma_buf < 0) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"vp9 get dma_buf error");
|
|
return -EFAULT;
|
|
}
|
|
|
|
pVeIommuBuf->attachment =
|
|
dma_buf_attach(pVeIommuBuf->dma_buf,
|
|
google_vp9_devp->platform_dev);
|
|
if (pVeIommuBuf->attachment < 0) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"vp9 get dma_buf_attachment error");
|
|
goto RELEASE_DMA_BUF;
|
|
}
|
|
|
|
sgt = dma_buf_map_attachment(pVeIommuBuf->attachment,
|
|
DMA_BIDIRECTIONAL);
|
|
|
|
sgt_bak = kmalloc(sizeof(struct sg_table), GFP_KERNEL | __GFP_ZERO);
|
|
if (sgt_bak == NULL)
|
|
return -ENOMEM;
|
|
|
|
ret = sg_alloc_table(sgt_bak, sgt->nents, GFP_KERNEL);
|
|
if (ret != 0)
|
|
google_vp9_printk(KERN_ERR,
|
|
"vp9 sg_alloc_table error");
|
|
|
|
sgl_bak = sgt_bak->sgl;
|
|
for_each_sg(sgt->sgl, sgl, sgt->nents, i) {
|
|
sg_set_page(sgl_bak, sg_page(sgl), sgl->length, sgl->offset);
|
|
sgl_bak = sg_next(sgl_bak);
|
|
}
|
|
|
|
pVeIommuBuf->sgt = sgt_bak;
|
|
if (pVeIommuBuf->sgt < 0) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"vp9 get sg_table error\n");
|
|
goto RELEASE_DMA_BUF;
|
|
}
|
|
|
|
ret = dma_map_sg(google_vp9_devp->platform_dev, pVeIommuBuf->sgt->sgl,
|
|
pVeIommuBuf->sgt->nents,
|
|
DMA_BIDIRECTIONAL);
|
|
if (ret != 1) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"vp9 dma_map_sg error\n");
|
|
goto RELEASE_DMA_BUF;
|
|
}
|
|
|
|
pVeIommuBuf->iommu_addr = sg_dma_address(pVeIommuBuf->sgt->sgl);
|
|
sUserIommuParam.iommu_addr = (unsigned int)(pVeIommuBuf->iommu_addr & 0xffffffff);
|
|
|
|
|
|
if (copy_to_user((void __user *)arg, &sUserIommuParam, sizeof(struct user_iommu_param))) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"vp9 get iommu copy_to_user error\n");
|
|
goto RELEASE_DMA_BUF;
|
|
}
|
|
|
|
pVeIommuBuf->p_id = current->tgid;
|
|
#if PRINTK_IOMMU_ADDR
|
|
google_vp9_printk(KERN_DEBUG,
|
|
"fd:%d, iommu_addr:%lx, dma_buf:%p, dma_buf_attach:%p, sg_table:%p, nents:%d, p_id:%d\n",
|
|
pVeIommuBuf->fd,
|
|
pVeIommuBuf->iommu_addr,
|
|
pVeIommuBuf->dma_buf,
|
|
pVeIommuBuf->attachment,
|
|
pVeIommuBuf->sgt,
|
|
pVeIommuBuf->sgt->nents,
|
|
pVeIommuBuf->p_id);
|
|
#endif
|
|
|
|
mutex_lock(&google_vp9_devp->lock_mem);
|
|
aw_mem_list_add_tail(&pVeIommuBuf->i_list, &google_vp9_devp->list);
|
|
mutex_unlock(&google_vp9_devp->lock_mem);
|
|
break;
|
|
|
|
RELEASE_DMA_BUF:
|
|
if (pVeIommuBuf->dma_buf > 0) {
|
|
if (pVeIommuBuf->attachment > 0) {
|
|
if (pVeIommuBuf->sgt > 0) {
|
|
dma_unmap_sg(google_vp9_devp->platform_dev,
|
|
pVeIommuBuf->sgt->sgl,
|
|
pVeIommuBuf->sgt->nents,
|
|
DMA_BIDIRECTIONAL);
|
|
dma_buf_unmap_attachment(pVeIommuBuf->attachment, pVeIommuBuf->sgt,
|
|
DMA_BIDIRECTIONAL);
|
|
sg_free_table(pVeIommuBuf->sgt);
|
|
kfree(pVeIommuBuf->sgt);
|
|
}
|
|
|
|
dma_buf_detach(pVeIommuBuf->dma_buf,
|
|
pVeIommuBuf->attachment);
|
|
}
|
|
|
|
dma_buf_put(pVeIommuBuf->dma_buf);
|
|
return -1;
|
|
}
|
|
kfree(pVeIommuBuf);
|
|
break;
|
|
}
|
|
case IOCTL_FREE_IOMMU_ADDR:
|
|
{
|
|
struct user_iommu_param sUserIommuParam;
|
|
struct cedarv_iommu_buffer *pVeIommuBuf;
|
|
|
|
if (copy_from_user(&sUserIommuParam, (void __user *)arg,
|
|
sizeof(struct user_iommu_param))) {
|
|
google_vp9_printk(KERN_ERR,
|
|
"IOCTL_FREE_IOMMU_ADDR copy_from_user error");
|
|
return -EFAULT;
|
|
}
|
|
aw_mem_list_for_each_entry(pVeIommuBuf, &google_vp9_devp->list, i_list) {
|
|
if (pVeIommuBuf->fd == sUserIommuParam.fd &&
|
|
pVeIommuBuf->p_id == current->tgid) {
|
|
#if PRINTK_IOMMU_ADDR
|
|
google_vp9_printk(KERN_DEBUG, "free: fd:%d, iommu_addr:%lx, dma_buf:%p, dma_buf_attach:%p, sg_table:%p, nets:%d, p_id:%d\n",
|
|
pVeIommuBuf->fd,
|
|
pVeIommuBuf->iommu_addr,
|
|
pVeIommuBuf->dma_buf,
|
|
pVeIommuBuf->attachment,
|
|
pVeIommuBuf->sgt,
|
|
pVeIommuBuf->sgt->nents,
|
|
pVeIommuBuf->p_id);
|
|
#endif
|
|
|
|
if (pVeIommuBuf->dma_buf > 0) {
|
|
if (pVeIommuBuf->attachment > 0) {
|
|
if (pVeIommuBuf->sgt > 0) {
|
|
dma_unmap_sg(google_vp9_devp->platform_dev,
|
|
pVeIommuBuf->sgt->sgl,
|
|
pVeIommuBuf->sgt->nents,
|
|
DMA_BIDIRECTIONAL);
|
|
dma_buf_unmap_attachment(pVeIommuBuf->attachment,
|
|
pVeIommuBuf->sgt,
|
|
DMA_BIDIRECTIONAL);
|
|
sg_free_table(pVeIommuBuf->sgt);
|
|
kfree(pVeIommuBuf->sgt);
|
|
}
|
|
|
|
dma_buf_detach(pVeIommuBuf->dma_buf, pVeIommuBuf->attachment);
|
|
}
|
|
|
|
dma_buf_put(pVeIommuBuf->dma_buf);
|
|
}
|
|
|
|
mutex_lock(&google_vp9_devp->lock_mem);
|
|
aw_mem_list_del(&pVeIommuBuf->i_list);
|
|
kfree(pVeIommuBuf);
|
|
mutex_unlock(&google_vp9_devp->lock_mem);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int googlevp9dev_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct google_vp9_info *info;
|
|
|
|
info = kmalloc(sizeof(struct google_vp9_info), GFP_KERNEL);
|
|
if (!info)
|
|
return -ENOMEM;
|
|
|
|
info->set_vol_flag = 0;
|
|
google_vp9_file = filp;
|
|
|
|
filp->private_data = info;
|
|
if (down_interruptible(&google_vp9_devp->sem))
|
|
return -ERESTARTSYS;
|
|
|
|
if (google_vp9_devp->ref_count == 0)
|
|
google_vp9_devp->de_irq_flag = 0;
|
|
|
|
up(&google_vp9_devp->sem);
|
|
nonseekable_open(inode, filp);
|
|
return 0;
|
|
}
|
|
|
|
static int googlevp9dev_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct google_vp9_info *info;
|
|
struct aw_mem_list_head *pos, *q;
|
|
struct cedarv_iommu_buffer *pVeIommuBuf;
|
|
|
|
info = filp->private_data;
|
|
if (google_vp9_devp->bMemDevAttachFlag) {
|
|
aw_mem_list_for_each_safe(pos, q, &google_vp9_devp->list) {
|
|
pVeIommuBuf = aw_mem_list_entry(pos, struct cedarv_iommu_buffer, i_list);
|
|
if (pVeIommuBuf->p_id == current->tgid) {
|
|
#if PRINTK_IOMMU_ADDR
|
|
google_vp9_printk(KERN_DEBUG, "free: fd:%d, iommu_addr:%lx, dma_buf:%p, dma_buf_attach:%p, sg_table:%p, nets:%d, p_id:%d\n",
|
|
pVeIommuBuf->fd,
|
|
pVeIommuBuf->iommu_addr,
|
|
pVeIommuBuf->dma_buf,
|
|
pVeIommuBuf->attachment,
|
|
pVeIommuBuf->sgt,
|
|
pVeIommuBuf->sgt->nents,
|
|
pVeIommuBuf->p_id);
|
|
#endif
|
|
if (pVeIommuBuf->dma_buf > 0) {
|
|
if (pVeIommuBuf->attachment > 0) {
|
|
if (pVeIommuBuf->sgt > 0) {
|
|
dma_unmap_sg(google_vp9_devp->platform_dev,
|
|
pVeIommuBuf->sgt->sgl,
|
|
pVeIommuBuf->sgt->nents,
|
|
DMA_BIDIRECTIONAL);
|
|
dma_buf_unmap_attachment(pVeIommuBuf->attachment,
|
|
pVeIommuBuf->sgt,
|
|
DMA_BIDIRECTIONAL);
|
|
sg_free_table(pVeIommuBuf->sgt);
|
|
kfree(pVeIommuBuf->sgt);
|
|
}
|
|
dma_buf_detach(pVeIommuBuf->dma_buf, pVeIommuBuf->attachment);
|
|
}
|
|
dma_buf_put(pVeIommuBuf->dma_buf);
|
|
}
|
|
mutex_lock(&google_vp9_devp->lock_mem);
|
|
aw_mem_list_del(&pVeIommuBuf->i_list);
|
|
kfree(pVeIommuBuf);
|
|
mutex_unlock(&google_vp9_devp->lock_mem);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (down_interruptible(&google_vp9_devp->sem))
|
|
return -ERESTARTSYS;
|
|
|
|
/* release other resource here */
|
|
if (google_vp9_devp->ref_count == 0)
|
|
google_vp9_devp->de_irq_flag = 1;
|
|
|
|
up(&google_vp9_devp->sem);
|
|
|
|
kfree(info);
|
|
google_vp9_file = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void googlevp9dev_vma_open(struct vm_area_struct *vma)
|
|
{
|
|
}
|
|
|
|
static void googlevp9dev_vma_close(struct vm_area_struct *vma)
|
|
{
|
|
}
|
|
|
|
static struct vm_operations_struct cedardev_remap_vm_ops = {
|
|
.open = googlevp9dev_vma_open,
|
|
.close = googlevp9dev_vma_close,
|
|
};
|
|
|
|
static int googlevp9dev_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
unsigned long temp_pfn;
|
|
|
|
if (vma->vm_end - vma->vm_start == 0) {
|
|
google_vp9_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)) {
|
|
google_vp9_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_VP9_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;
|
|
googlevp9dev_vma_open(vma);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int snd_sw_google_vp9_suspend(struct platform_device *pdev,
|
|
pm_message_t state)
|
|
{
|
|
int ret = 0;
|
|
|
|
google_vp9_printk(KERN_WARNING, "[google_vp9] standby suspend\n");
|
|
ret = disable_google_vp9_hw_clk();
|
|
|
|
if (ret < 0) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"google_vp9 clk disable somewhere error!\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int snd_sw_google_vp9_resume(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
|
|
google_vp9_printk(KERN_WARNING, "[google_vp9] standby resume\n");
|
|
|
|
if (google_vp9_devp->ref_count == 0)
|
|
return 0;
|
|
|
|
ret = enable_google_vp9_hw_clk();
|
|
if (ret < 0) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"google_vp9 clk enable somewhere error!\n");
|
|
return -EFAULT;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct file_operations googlevp9dev_fops = {
|
|
.owner = THIS_MODULE,
|
|
.mmap = googlevp9dev_mmap,
|
|
.open = googlevp9dev_open,
|
|
.release = googlevp9dev_release,
|
|
.llseek = no_llseek,
|
|
.unlocked_ioctl = compat_googlevp9dev_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = compat_googlevp9dev_ioctl,
|
|
#endif
|
|
};
|
|
|
|
static int googleVp9dev_init(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
int devno;
|
|
|
|
#if defined(CONFIG_OF)
|
|
struct device_node *node;
|
|
#endif
|
|
dev_t dev;
|
|
|
|
dev = 0;
|
|
|
|
google_vp9_printk(KERN_WARNING, "[google vp9]: install start!!!\n");
|
|
|
|
|
|
#if defined(CONFIG_OF)
|
|
node = pdev->dev.of_node;
|
|
#endif
|
|
|
|
/*register or alloc the device number.*/
|
|
if (vp9_dev_major) {
|
|
dev = MKDEV(vp9_dev_major, vp9_dev_minor);
|
|
ret = register_chrdev_region(dev, 1, "googlevp9_dev");
|
|
} else {
|
|
ret = alloc_chrdev_region(&dev, vp9_dev_minor, 1, "googlevp9_dev");
|
|
vp9_dev_major = MAJOR(dev);
|
|
vp9_dev_minor = MINOR(dev);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"googlevp9_dev: can't get major %d\n",
|
|
vp9_dev_major);
|
|
return ret;
|
|
}
|
|
spin_lock_init(&google_vp9_spin_lock);
|
|
google_vp9_devp = kmalloc(sizeof(struct googlevp9_dev), GFP_KERNEL);
|
|
if (google_vp9_devp == NULL) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"malloc mem for google vp9 device err\n");
|
|
return -ENOMEM;
|
|
}
|
|
memset(google_vp9_devp, 0, sizeof(struct googlevp9_dev));
|
|
|
|
#if defined(CONFIG_OF)
|
|
google_vp9_devp->irq = irq_of_parse_and_map(node, 0);
|
|
google_vp9_printk(KERN_INFO, "google vp9: the get irq is %d\n",
|
|
google_vp9_devp->irq);
|
|
if (google_vp9_devp->irq <= 0)
|
|
google_vp9_printk(KERN_WARNING, "Can't parse IRQ");
|
|
#else
|
|
google_vp9_devp->irq = SUNXI_IRQ_GOOGLE_VP9;
|
|
#endif
|
|
|
|
sema_init(&google_vp9_devp->sem, 1);
|
|
init_waitqueue_head(&google_vp9_devp->wq);
|
|
|
|
memset(&google_vp9_devp->iomap_addrs, 0, sizeof(struct iomap_para));
|
|
|
|
ret = request_irq(google_vp9_devp->irq,
|
|
GoogleVp9Interrupt, 0, "googlevp9_dev", NULL);
|
|
if (ret < 0) {
|
|
google_vp9_printk(KERN_WARNING, "request irq err\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* map for macc io space */
|
|
#if defined(CONFIG_OF)
|
|
google_vp9_devp->iomap_addrs.regs_macc = of_iomap(node, 0);
|
|
if (!google_vp9_devp->iomap_addrs.regs_macc)
|
|
google_vp9_printk(KERN_WARNING, "vp9 Can't map registers");
|
|
|
|
google_vp9_devp->sram_bass_vir = (unsigned int *)of_iomap(node, 1);
|
|
if (!google_vp9_devp->sram_bass_vir)
|
|
google_vp9_printk(KERN_WARNING,
|
|
"vp9 Can't map sram_bass_vir registers");
|
|
|
|
google_vp9_devp->clk_bass_vir = (unsigned int *)of_iomap(node, 2);
|
|
if (!google_vp9_devp->clk_bass_vir)
|
|
google_vp9_printk(KERN_WARNING,
|
|
"vp9 Can't map clk_bass_vir registers");
|
|
#endif
|
|
|
|
#if defined(CONFIG_OF)
|
|
google_vp9_parent_pll_clk = of_clk_get(node, 0);
|
|
if ((!google_vp9_parent_pll_clk) || IS_ERR(google_vp9_parent_pll_clk)) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"try to get google_vp9_parent_pll_clk fail\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
google_vp9_moduleclk = of_clk_get(node, 1);
|
|
if (!google_vp9_moduleclk || IS_ERR(google_vp9_moduleclk))
|
|
google_vp9_printk(KERN_WARNING, "get google_vp9_moduleclk failed;\n");
|
|
#endif
|
|
|
|
|
|
google_vp9_devp->platform_dev = &pdev->dev;
|
|
|
|
sunxi_periph_reset_assert(google_vp9_moduleclk);
|
|
clk_prepare(google_vp9_moduleclk);
|
|
|
|
/* Create char device */
|
|
devno = MKDEV(vp9_dev_major, vp9_dev_minor);
|
|
cdev_init(&google_vp9_devp->cdev, &googlevp9dev_fops);
|
|
google_vp9_devp->cdev.owner = THIS_MODULE;
|
|
ret = cdev_add(&google_vp9_devp->cdev, devno, 1);
|
|
if (ret)
|
|
google_vp9_printk(KERN_WARNING, "Err:%d add cedardev", ret);
|
|
|
|
google_vp9_devp->class = class_create(THIS_MODULE, "googlevp9_dev");
|
|
google_vp9_devp->dev =
|
|
device_create(google_vp9_devp->class,
|
|
NULL, devno, NULL, "googlevp9_dev");
|
|
|
|
setup_timer(&google_vp9_devp->vp9_engine_timer,
|
|
vp9_engine_for_events, (unsigned long)google_vp9_devp);
|
|
setup_timer(&google_vp9_devp->vp9_engine_timer_rel,
|
|
vp9_engine_for_timer_rel, (unsigned long)google_vp9_devp);
|
|
|
|
mutex_init(&google_vp9_devp->lock_mem);
|
|
|
|
google_vp9_printk(KERN_WARNING, "[cedar]: install end!!!\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void googleVp9dev_exit(void)
|
|
{
|
|
dev_t dev;
|
|
|
|
dev = MKDEV(vp9_dev_major, vp9_dev_minor);
|
|
|
|
free_irq(google_vp9_devp->irq, NULL);
|
|
iounmap(google_vp9_devp->iomap_addrs.regs_macc);
|
|
/* Destroy char device */
|
|
if (google_vp9_devp) {
|
|
cdev_del(&google_vp9_devp->cdev);
|
|
device_destroy(google_vp9_devp->class, dev);
|
|
class_destroy(google_vp9_devp->class);
|
|
}
|
|
|
|
if (google_vp9_moduleclk == NULL ||
|
|
IS_ERR(google_vp9_moduleclk)) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"google_vp9_moduleclk handle is invalid,just return!\n");
|
|
} else {
|
|
clk_disable_unprepare(google_vp9_moduleclk);
|
|
clk_put(google_vp9_moduleclk);
|
|
google_vp9_moduleclk = NULL;
|
|
}
|
|
|
|
if (google_vp9_parent_pll_clk == NULL ||
|
|
IS_ERR(google_vp9_parent_pll_clk)) {
|
|
google_vp9_printk(KERN_WARNING,
|
|
"google_vp9_parent_pll_clk ,handle is invalid,just return!\n");
|
|
} else {
|
|
clk_put(google_vp9_parent_pll_clk);
|
|
}
|
|
|
|
unregister_chrdev_region(dev, 1);
|
|
kfree(google_vp9_devp);
|
|
}
|
|
|
|
static int sunxi_google_vp9_remove(struct platform_device *pdev)
|
|
{
|
|
googleVp9dev_exit();
|
|
return 0;
|
|
}
|
|
|
|
static int sunxi_google_vp9_probe(struct platform_device *pdev)
|
|
{
|
|
googleVp9dev_init(pdev);
|
|
return 0;
|
|
}
|
|
|
|
/*share the irq no. with timer2*/
|
|
/*static struct resource sunxi_cedar_resource[] = {
|
|
* [0] = {
|
|
.start = SUNXI_IRQ_GOOGLE_VP9,
|
|
.end = SUNXI_IRQ_GOOGLE_VP9,
|
|
.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_google_vp9_driver = {
|
|
.probe = sunxi_google_vp9_probe,
|
|
.remove = sunxi_google_vp9_remove,
|
|
#ifdef CONFIG_PM
|
|
.suspend = snd_sw_google_vp9_suspend,
|
|
.resume = snd_sw_google_vp9_resume,
|
|
#endif
|
|
.driver = {
|
|
.name = "sunxi-google-vp9",
|
|
.owner = THIS_MODULE,
|
|
|
|
#if defined(CONFIG_OF)
|
|
.of_match_table = sunxi_google_vp9_match,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
static int __init sunxi_google_vp9_init(void)
|
|
{
|
|
google_vp9_printk(KERN_WARNING, "sunxi google vp9 version 0.1\n");
|
|
return platform_driver_register(&sunxi_google_vp9_driver);
|
|
}
|
|
|
|
static void __exit sunxi_google_vp9_exit(void)
|
|
{
|
|
platform_driver_unregister(&sunxi_google_vp9_driver);
|
|
}
|
|
|
|
module_init(sunxi_google_vp9_init);
|
|
module_exit(sunxi_google_vp9_exit);
|
|
|
|
|
|
MODULE_AUTHOR("Soft-Reuuimlla");
|
|
MODULE_DESCRIPTION("User mode GOOGLE VP9 device interface");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(DRV_VERSION);
|
|
MODULE_ALIAS("platform:cedarx-sunxi");
|
|
|