Files
2026-08-24 10:03:24 +08:00

1766 lines
53 KiB
C

/*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*/
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/version.h>
#include <linux/kfifo.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/dma-buf.h>
#include <linux/reset.h>
#include <linux/pm_runtime.h>
#include <linux/of_address.h>
#include <linux/poll.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include "tahoe.h"
#include "tahoe_register.h"
#define IRQ_FRM_DONE_FBC 0x1
#define IRQ_FRM_START 0x2
#define IRQ_SCR_WRITE 0x4
#define IRQ_RDMA_DONE 0x8
#define IRQ_PRELOAD_DONE 0x10
#define IRQ_ROWRC_1 0x20
#define IRQ_ROWRC_2 0x40
#define IRQ_FRM_DONE_EC 0x80
#define IRQ_FRM_DONE_LPF 0x100
#define IRQ_FRM_DONE_RDO 0x200
#define IRQ_FRM_DONE_ME 0x400
#define IRQ_SLICE_DONE_FBC 0x800
#define IRQ_SLICE_DONE_EC 0x1000
#define IRQ_SLICE_DONE_LPF 0x2000
#define IRQ_SLICE_DONE_RDO 0x4000
#define IRQ_SLICE_DONE_ME 0x8000
#define IRQ_ES_OVERFLOW 0x10000
#define IRQ_RESERVED 0x20000
#define IRQ_LOW_LATENCY 0x40000
#define DRIVER_NAME "tahoe"
#define DEVICE_NAME "tahoe"
#define CLASS_NAME "tahoe"
#define LOG_ALL 0
#define LOG_INFO 1
#define LOG_DEBUG 2
#define LOG_ERROR 3
MODULE_IMPORT_NS(DMA_BUF);
static s32 print_level = LOG_ERROR;
#define enc_pr(level, x...) \
do { \
if (level >= print_level) \
printk(x); \
} while (0)
static s32 clock_gate_count = 0;
static struct venc_drv_buffer_t fw_buffer; //fw addr
#define MC_SIZE (4096 * 1024)
#define MAX_INSTANCE 4
static int s_interrupt_flag;
static wait_queue_head_t s_interrupt_wait_q;
static struct venc_drv_context_t s_tahoe_drv_context;
static s32 s_tahoe_major;
static s32 s_register_flag;
static struct device *tahoe_dev;
static struct platform_device *tahoe_pdev;
static s32 s_tahoe_open_ref_count;
static int s_tahoe_irq;
static bool s_tahoe_irq_requested;
static spinlock_t s_vpu_lock = __SPIN_LOCK_UNLOCKED(s_vpu_lock);
#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 3, 13)
static DEFINE_SEMAPHORE(s_vpu_sem);
#else
static DEFINE_SEMAPHORE(s_vpu_sem, 1);
#endif
static struct list_head s_vbp_head = LIST_HEAD_INIT(s_vbp_head);
static struct venc_drv_common_buffer_t s_video_memory[MAX_INSTANCE] = {0}; //每个通道单独申请36M,传到固件中;
static struct venc_drv_buffer_t s_tahoe_register = {0};
static bool use_reserve;
static u32 cma_cfg_size;
static struct tasklet_struct tahoe_tasklet;
static struct venc_bit_firmware_info_t s_bit_firmware_info;
static spinlock_t s_dma_buf_lock = __SPIN_LOCK_UNLOCKED(s_dma_buf_lock);
static struct list_head s_dma_bufp_head = LIST_HEAD_INIT(s_dma_bufp_head);
static u32 clock_a;
struct tahoe_clks {
struct clk *sys_tahoe;
struct clk *tahoe_clk;
};
static struct tahoe_clks s_tahoe_clks;
struct tahoe_rsts {
struct reset_control *core_rst;
struct reset_control *a_rst;
struct reset_control *apb_rst;
struct reset_control *sys_rst;
};
static struct tahoe_rsts s_tahoe_rsts;
static void tahoe_clk_enable(struct tahoe_clks *clks);
static void tahoe_clk_disable(struct tahoe_clks *clks);
static s32 tahoe_free_buffers(struct file *filp);
static void dma_flush(u32 buf_start, u32 buf_size)
{
if (tahoe_pdev)
dma_sync_single_for_device(
&tahoe_pdev->dev, buf_start,
buf_size, DMA_TO_DEVICE);
}
static void cache_flush(u32 buf_start, u32 buf_size)
{
if (tahoe_pdev)
dma_sync_single_for_cpu(
&tahoe_pdev->dev, buf_start,
buf_size, DMA_FROM_DEVICE);
}
//__need_modify__
static void tahoe_isr_tasklet(ulong data)
{
#if 0
u32 intr_reason;
struct venc_drv_context_t *dev = (struct venc_drv_context_t *)data;
/* notify the interrupt to user space */
if (dev->async_queue) {
enc_pr(LOG_ALL, "kill_fasync e %s\n", __func__);
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
intr_reason = dev->interrupt_flag;
if (intr_reason) {
dev->interrupt_flag = 0;
enc_pr(LOG_INFO, "isr_tasklet intr:0x%08x\n", intr_reason);
s_interrupt_flag = 1;
wake_up_interruptible(&s_interrupt_wait_q);
}
#endif
struct venc_drv_context_t *s_context = (struct venc_drv_context_t *)data;
atomic_inc(&s_context->request_ready);
wake_up_interruptible(&s_context->request_complete);
enc_pr(LOG_ALL, "[-]%s\n", __func__);
}
//__need_modify__
static irqreturn_t tahoe_irq_handler(int irq, void *dev_id)
{
struct venc_drv_context_t *dev = (struct venc_drv_context_t *)dev_id;
dev->interrupt_reason = ReadTahoeRegister(TAHOE_VRISC_IRQ_STAT);
enc_pr(LOG_INFO, "interrupt_reason 0x%lx\n", dev->interrupt_reason);
WriteTahoeRegister(TAHOE_VRISC_IRQ_STAT, ~dev->interrupt_reason);
if (dev->interrupt_reason & IRQ_PRELOAD_DONE) {
//preload_irq_flag = 1;
enc_pr(LOG_INFO, "preload irq received, preload done\n");
}
if (dev->interrupt_reason & IRQ_SCR_WRITE) {
//scrw_irq_flag = 1;
enc_pr(LOG_INFO, "scr write irq received\n");
}
if (dev->interrupt_reason & IRQ_FRM_DONE_FBC) {
enc_pr(LOG_INFO, "frame done irq received\n");
tasklet_schedule(&tahoe_tasklet);
}
//tasklet_schedule(&tahoe_tasklet);
enc_pr(LOG_ALL, "[-]%s\n", __func__);
//wake_up_interruptible(&s_interrupt_wait_q);
return IRQ_HANDLED;
}
static int tahoe_dma_buffer_map(struct tahoe_dma_cfg *cfg)
{
int ret = -1;
int fd = -1;
struct page *page = NULL;
struct dma_buf *dbuf = NULL;
struct dma_buf_attachment *d_att = NULL;
struct sg_table *sg = NULL;
void *vaddr = NULL;
struct device *dev = NULL;
enum dma_data_direction dir;
if (cfg == NULL || (cfg->fd < 0) || cfg->dev == NULL) {
enc_pr(LOG_ERROR, "error dma param\n");
return -EINVAL;
}
fd = cfg->fd;
dev = cfg->dev;
dir = cfg->dir;
dbuf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(dbuf)) {
enc_pr(LOG_ERROR, "failed to get dma buffer,fd %d\n",fd);
return -EINVAL;
}
d_att = dma_buf_attach(dbuf, dev);
if (IS_ERR(d_att)) {
enc_pr(LOG_ERROR, "failed to set dma attach\n");
goto attach_err;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
sg = dma_buf_map_attachment(d_att, dir);
#else
sg = dma_buf_map_attachment_unlocked(d_att, dir);
#endif
if (IS_ERR(sg)) {
enc_pr(LOG_ERROR, "failed to get dma sg\n");
goto map_attach_err;
}
page = sg_page(sg->sgl);
cfg->paddr = PFN_PHYS(page_to_pfn(page));
cfg->dbuf = dbuf;
cfg->attach = d_att;
cfg->vaddr = vaddr;
cfg->sg = sg;
return 0;
map_attach_err:
dma_buf_detach(dbuf, d_att);
attach_err:
dma_buf_put(dbuf);
return ret;
}
static void tahoe_dma_buffer_unmap(struct tahoe_dma_cfg *cfg)
{
int fd = -1;
struct dma_buf *dbuf = NULL;
struct dma_buf_attachment *d_att = NULL;
struct sg_table *sg = NULL;
/*void *vaddr = NULL;*/
struct device *dev = NULL;
enum dma_data_direction dir;
if (cfg == NULL || (cfg->fd < 0) || cfg->dev == NULL
|| cfg->dbuf == NULL /*|| cfg->vaddr == NULL*/
|| cfg->attach == NULL || cfg->sg == NULL) {
enc_pr(LOG_ERROR, "unmap: Error dma param\n");
return;
}
fd = cfg->fd;
dev = cfg->dev;
dir = cfg->dir;
dbuf = cfg->dbuf;
d_att = cfg->attach;
sg = cfg->sg;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
dma_buf_unmap_attachment(d_att, sg, dir);
#else
dma_buf_unmap_attachment_unlocked(d_att, sg, dir);
#endif
dma_buf_detach(dbuf, d_att);
dma_buf_put(dbuf);
enc_pr(LOG_INFO, "vpu_dma_buffer_unmap fd %d\n",fd);
}
static s32 tahoe_dma_buffer_get_phys(struct tahoe_dma_cfg *cfg,
unsigned long *addr)
{
int ret = 0;
if (cfg->paddr == 0)
{ /* only mapp once */
ret = tahoe_dma_buffer_map(cfg);
if (ret < 0) {
enc_pr(LOG_ERROR, "vpu_dma_buffer_map failed\n");
return ret;
}
}
if (cfg->paddr) *addr = cfg->paddr;
enc_pr(LOG_INFO,"vpu_dma_buffer_get_phys 0x%lx\n", cfg->paddr);
return ret;
}
static s32 tahoe_src_addr_config(struct tahoe_dma_buf_info_t *pinfo,
struct file *filp)
{
struct tahoe_dma_buf_pool_t *vbp;
unsigned long phy_addr;
struct tahoe_dma_cfg *cfg;
s32 idx, ret = 0;
if (pinfo->num_planes == 0 || pinfo->num_planes > 3)
return -EFAULT;
for (idx = 0; idx < pinfo->num_planes; idx++)
pinfo->phys_addr[idx] = 0;
for (idx = 0; idx < pinfo->num_planes; idx++) {
vbp = kzalloc(sizeof(*vbp), GFP_KERNEL);
if (!vbp) {
ret = -ENOMEM;
break;
}
memset(vbp, 0, sizeof(struct tahoe_dma_buf_pool_t));
cfg = &vbp->dma_cfg;
cfg->dir = DMA_TO_DEVICE;
cfg->fd = pinfo->fd[idx];
cfg->dev = &(tahoe_pdev->dev);
phy_addr = 0;
ret = tahoe_dma_buffer_get_phys(cfg, &phy_addr);
if (ret < 0) {
enc_pr(LOG_ERROR, "import fd %d failed\n", cfg->fd);
kfree(vbp);
ret = -1;
break;
}
pinfo->phys_addr[idx] = (ulong) phy_addr;
vbp->filp = filp;
spin_lock(&s_dma_buf_lock);
list_add(&vbp->list, &s_dma_bufp_head);
spin_unlock(&s_dma_buf_lock);
}
enc_pr(LOG_INFO, "vpu_src_addr_config phy_addr 0x%lx, 0x%lx, 0x%lx\n",
pinfo->phys_addr[0], pinfo->phys_addr[1], pinfo->phys_addr[2]);
//hoan add for canvas test
#if 0
phys_addrY = pinfo->phys_addr[0];
phys_addrU = pinfo->phys_addr[1];
phys_addrV = pinfo->phys_addr[2];
#endif
//end
/*
* Variable vbp will free in vpu_dma_buf_release finally.
*/
/* coverity[leaked_storage] */
return ret;
}
static s32 tahoe_src_addr_unmap(struct tahoe_dma_buf_info_t *pinfo,
struct file *filp)
{
struct tahoe_dma_buf_pool_t *pool, *n;
struct tahoe_dma_cfg vb;
ulong phys_addr;
s32 plane_idx = 0;
s32 ret = 0;
s32 found;
if (pinfo->num_planes == 0 || pinfo->num_planes > 3)
return -EFAULT;
enc_pr(LOG_INFO,
"dma_unmap planes %d fd: %d-%d-%d, phy_add: 0x%lx-%lx-%lx\n",
pinfo->num_planes, pinfo->fd[0],pinfo->fd[1], pinfo->fd[2],
pinfo->phys_addr[0], pinfo->phys_addr[1], pinfo->phys_addr[2]);
list_for_each_entry_safe(pool, n, &s_dma_bufp_head, list) {
found = 0;
if (pool->filp == filp) {
vb = pool->dma_cfg;
phys_addr = vb.paddr;
if (vb.fd == pinfo->fd[0])
{
if (phys_addr != pinfo->phys_addr[0]) {
enc_pr(LOG_ERROR, "dma_unmap plane 0");
enc_pr(LOG_ERROR, " no match ");
enc_pr(LOG_ERROR, "0x%lx %lx\n",
phys_addr, pinfo->phys_addr[0]);
}
found = 1;
plane_idx++;
}
else if (vb.fd == pinfo->fd[1]
&& pinfo->num_planes > 1) {
if (phys_addr != pinfo->phys_addr[1]) {
enc_pr(LOG_ERROR, "dma_unmap plane 1");
enc_pr(LOG_ERROR, " no match ");
enc_pr(LOG_ERROR, "0x%lx %lx\n",
phys_addr, pinfo->phys_addr[1]);
}
plane_idx++;
found = 1;
}
else if (vb.fd == pinfo->fd[2]
&& pinfo->num_planes > 2) {
if (phys_addr != pinfo->phys_addr[2]) {
enc_pr(LOG_ERROR, "dma_unmap plane 2");
enc_pr(LOG_ERROR, " no match ");
enc_pr(LOG_ERROR, "0x%lx %lx\n",
phys_addr, pinfo->phys_addr[2]);
}
plane_idx++;
found = 1;
}
if (found && vb.attach) {
tahoe_dma_buffer_unmap(&vb);
spin_lock(&s_dma_buf_lock);
list_del(&pool->list);
spin_unlock(&s_dma_buf_lock);
kfree(pool);
}
}
}
if (plane_idx != pinfo->num_planes) {
enc_pr(LOG_DEBUG, "dma_unmap fd planes not match\n");
enc_pr(LOG_DEBUG, " found %d need %d\n",
plane_idx, pinfo->num_planes);
}
return ret;
}
static s32 tahoe_dma_buf_release(struct file *filp)
{
struct tahoe_dma_buf_pool_t *pool, *n;
struct tahoe_dma_cfg vb;
enc_pr(LOG_DEBUG, "tahoe_dma_buf_release\n");
list_for_each_entry_safe(pool, n, &s_dma_bufp_head, list) {
if (pool->filp == filp) {
vb = pool->dma_cfg;
if (vb.attach) {
tahoe_dma_buffer_unmap(&vb);
spin_lock(&s_dma_buf_lock);
list_del(&pool->list);
spin_unlock(&s_dma_buf_lock);
kfree(pool);
}
}
}
return 0;
}
static void tahoe_top_sw_reset(void)
{
WriteTahoeRegister(TAHOE_TOP_SW_RST, 0x3FFF);
WriteTahoeRegister(TAHOE_TOP_SW_RST, 0);
}
static s32 tahoe_open(struct inode *inode, struct file *filp)
{
bool first_open = false;
s32 r = 0;
//enc_pr(LOG_DEBUG, "[+] %s, filp=%lu, %lu, f_count=%lld\n", __func__,
//(unsigned long)filp, ( ((unsigned long)filp)%8), filp->f_count.counter);
spin_lock(&s_vpu_lock);
s_tahoe_drv_context.open_count++;
if (s_tahoe_drv_context.open_count == 1) {
first_open = true;
}
init_waitqueue_head(&s_tahoe_drv_context.request_complete);
atomic_set(&s_tahoe_drv_context.request_ready, 0);
filp->private_data = (void *)(&s_tahoe_drv_context);
spin_unlock(&s_vpu_lock);
if (first_open) {
if ((s_tahoe_irq >= 0) && (s_tahoe_irq_requested == false)) {
s32 err;
err = request_irq(s_tahoe_irq, tahoe_irq_handler, 0, "tahoe_irq0", (void *)(&s_tahoe_drv_context));
if (err) {
enc_pr(LOG_ERROR, "Failed to register irq handler\n");
spin_lock(&s_vpu_lock);
s_tahoe_drv_context.open_count--;
spin_unlock(&s_vpu_lock);
return -EFAULT;
}
s_tahoe_irq_requested = true;
}
/* enable tahoe clks and power*/
tahoe_clk_enable(&s_tahoe_clks);
tahoe_top_sw_reset();
}
if (r != 0) {
spin_lock(&s_vpu_lock);
s_tahoe_drv_context.open_count--;
spin_unlock(&s_vpu_lock);
}
enc_pr(LOG_DEBUG, "[-] %s, ret: %d\n", __func__, r);
return r;
}
static s32 tahoe_release(struct inode *inode, struct file *filp)
{
s32 ret = 0;
u32 open_count;
int i;
//enc_pr(LOG_DEBUG, "vpu_release filp=%lu, f_counter=%ld\n",
//(unsigned long)filp, filp->f_count.counter);
ret = down_interruptible(&s_vpu_sem);
if (ret == 0) {
/* found and free the not handled
buffer by user applications */
tahoe_free_buffers(filp);
tahoe_dma_buf_release(filp);
//tahoe_dma_buf_release(filp); //__need_modify__
spin_lock(&s_vpu_lock);
s_tahoe_drv_context.open_count--;
open_count = s_tahoe_drv_context.open_count;
spin_unlock(&s_vpu_lock);
enc_pr(LOG_INFO, "open_count=%u\n", open_count);
if (open_count == 0) {
s_tahoe_drv_context.interrupt_reason = 0;
s_tahoe_drv_context.interrupt_flag = 0;
for (i = 0; i < MAX_INSTANCE; i++) {
if (s_video_memory[i].phys_addr && !use_reserve) {
enc_pr(LOG_DEBUG,
"tahoe_release, s_video_memory 0x%lx\n",
s_video_memory[i].phys_addr);
codec_mm_free_for_dma(
DEVICE_NAME,
s_video_memory[i].phys_addr);
memset(&s_video_memory[i],
0, sizeof(struct venc_drv_common_buffer_t));
}
}
if (fw_buffer.phys_addr) {
enc_pr(LOG_DEBUG,
"tahoe_release, fw_buffer.phys_addr 0x%lx\n",
fw_buffer.phys_addr);
codec_mm_free_for_dma(
DEVICE_NAME,
fw_buffer.phys_addr);
memset(&fw_buffer,
0, sizeof(struct venc_drv_buffer_t));
}
if ((s_tahoe_irq >= 0)
&& (s_tahoe_irq_requested == true)) {
free_irq(s_tahoe_irq, &s_tahoe_drv_context);
s_tahoe_irq_requested = false;
}
/* disable tahoe clks.*/
tahoe_clk_disable(&s_tahoe_clks);
}
}
up(&s_vpu_sem);
return 0;
}
static s32 tahoe_mmap(struct file *filp, struct vm_area_struct *vma)
{
ulong vm_size = (unsigned long)(vma->vm_end - vma->vm_start);
/*if (vma->vm_pgoff == fw_buffer.phys_addr >> PAGE_SHIFT) {
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
} else {*/
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); //__need_modify__
//}
return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vm_size, vma->vm_page_prot) ? -EAGAIN : 0;
}
static s32 tahoe_alloc_dma_buffer(struct venc_drv_buffer_t *vb)
{
if (!vb)
return -1;
vb->phys_addr = (ulong)codec_mm_alloc_for_dma(DEVICE_NAME, PAGE_ALIGN(vb->size) >> PAGE_SHIFT, 0, 0);
if (vb->phys_addr == 0) {
enc_pr(LOG_ERROR,
"Physical memory allocation error size=%d\n",
vb->size);
return -1;
}
enc_pr(LOG_INFO,
"%s: vb->phys_addr 0x%lx\n", __func__,
vb->phys_addr);
return 0;
}
static void tahoe_free_dma_buffer(struct venc_drv_buffer_t *vb)
{
if (!vb)
return;
enc_pr(LOG_INFO, "tahoe_free_dma_buffer 0x%lx\n",vb->phys_addr);
if (vb->phys_addr)
codec_mm_free_for_dma(DEVICE_NAME, vb->phys_addr);
}
static s32 tahoe_free_buffers(struct file *filp)
{
struct venc_drv_buffer_pool_t *pool, *n;
struct venc_drv_buffer_t vb;
enc_pr(LOG_DEBUG, "tahoe_free_buffers\n");
list_for_each_entry_safe(pool, n, &s_vbp_head, list) {
if (pool->filp == filp) {
vb = pool->vb;
if (vb.phys_addr) {
tahoe_free_dma_buffer(&vb);
spin_lock(&s_vpu_lock);
list_del(&pool->list);
spin_unlock(&s_vpu_lock);
kfree(pool);
}
}
}
return 0;
}
static long tahoe_ioctl(struct file *filp, u32 cmd, ulong arg)
{
s32 ret = 0;
struct venc_drv_context_t *dev = (struct venc_drv_context_t *)filp->private_data;
switch (cmd)
{
case IOC_GET_REGISTER_INFO:
{
enc_pr(LOG_ALL, "[+]IOC_GET_REGISTER_INFO\n");
ret = copy_to_user((void __user *)arg, &s_tahoe_register, sizeof(struct venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
enc_pr(LOG_ALL, "[-]IOC_GET_REGISTER_INFO\n");
}
break;
case IOC_GET_COMMON_BUF_INFO:
{
int chnid;
struct venc_drv_common_buffer_t info;
enc_pr(LOG_ALL, "[+]IOC_GET_COMMON_BUF_INFO\n");
ret = copy_from_user(&info, (struct venc_drv_common_buffer_t *)arg, sizeof(struct venc_drv_common_buffer_t));
if (ret != 0)
return -EFAULT;
chnid = info.chnid;
if (chnid < 0 || chnid >= MAX_INSTANCE)
return -EFAULT;
s_video_memory[chnid].chnid = chnid;
if (s_video_memory[chnid].phys_addr != 0) {
ret = copy_to_user((void __user *)arg,
&s_video_memory[chnid],
sizeof(struct venc_drv_common_buffer_t));
if (ret != 0)
ret = -EFAULT;
} else {
ret = down_interruptible(&s_vpu_sem);
if (ret != 0)
break;
s_video_memory[chnid].size = cma_cfg_size;
s_video_memory[chnid].phys_addr = (ulong)codec_mm_alloc_for_dma(DEVICE_NAME, cma_cfg_size >> PAGE_SHIFT, 0, 0);
if (s_video_memory[chnid].phys_addr) {
s_video_memory[chnid].base = s_video_memory[chnid].phys_addr;
enc_pr(LOG_DEBUG, "allocating phys 0x%lx, ", s_video_memory[chnid].phys_addr);
enc_pr(LOG_DEBUG, "virt addr 0x%lx, size %dk\n", s_video_memory[chnid].base, s_video_memory[chnid].size >> 10);
} else {
enc_pr(LOG_ERROR, "Failed to alloc dma buffer %s, phys:0x%lx\n", DEVICE_NAME, s_video_memory[chnid].phys_addr);
if (s_video_memory[chnid].phys_addr)
codec_mm_free_for_dma(DEVICE_NAME, s_video_memory[chnid].phys_addr);
s_video_memory[chnid].phys_addr = 0;
ret = -ENOMEM;
up(&s_vpu_sem);
break;
}
ret = copy_to_user((void __user *)arg,
&s_video_memory[chnid],
sizeof(struct venc_drv_common_buffer_t));
if (ret != 0)
ret = -EFAULT;
up(&s_vpu_sem);
}
enc_pr(LOG_ALL,
"[-]IOC_GET_COMMON_BUF_INFO\n");
}
break;
#ifdef CONFIG_COMPAT
case IOC_GET_COMMON_BUF_INFO32:
{
struct compat_venc_drv_buffer_t buf32;
int chnid;
struct venc_drv_common_buffer_t info;
enc_pr(LOG_ALL,
"[+]IOC_GET_COMMON_BUF_INFO32\n");
ret = copy_from_user(&info, (struct venc_drv_common_buffer_t *)arg, sizeof(struct venc_drv_common_buffer_t));
if (ret != 0)
return -EFAULT;
chnid = info.chnid;
if (chnid < 0 || chnid >= MAX_INSTANCE)
return -EFAULT;
s_video_memory[chnid].chnid = chnid;
memset(&buf32, 0, sizeof(struct compat_venc_drv_buffer_t));
buf32.size = s_video_memory[chnid].size;
buf32.phys_addr =
(compat_ulong_t)
s_video_memory[chnid].phys_addr;
buf32.virt_addr =
(compat_ulong_t)
s_video_memory[chnid].virt_addr;
if (s_video_memory[chnid].phys_addr != 0) {
ret = copy_to_user((void __user *)arg,
&buf32,
sizeof(struct compat_venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
} else {
ret = down_interruptible(&s_vpu_sem);
if (ret != 0)
break;
s_video_memory[chnid].size = cma_cfg_size;
s_video_memory[chnid].phys_addr = (ulong)codec_mm_alloc_for_dma(DEVICE_NAME, cma_cfg_size >> PAGE_SHIFT, 0, 0);
if (s_video_memory[chnid].phys_addr) {
s_video_memory[chnid].base = s_video_memory[chnid].phys_addr;
enc_pr(LOG_DEBUG, "allocating phys 0x%lx, ", s_video_memory[chnid].phys_addr);
enc_pr(LOG_DEBUG, "virt addr 0x%lx, size %dk\n", s_video_memory[chnid].base, s_video_memory[chnid].size >> 10);
} else {
enc_pr(LOG_ERROR, "Failed to alloc dma buffer %s, phys:0x%lx\n", DEVICE_NAME, s_video_memory[chnid].phys_addr);
if (s_video_memory[chnid].phys_addr)
codec_mm_free_for_dma(DEVICE_NAME, s_video_memory[chnid].phys_addr);
s_video_memory[chnid].phys_addr = 0;
ret = -ENOMEM;
up(&s_vpu_sem);
break;
}
buf32.size = s_video_memory[chnid].size;
buf32.phys_addr =
(compat_ulong_t)
s_video_memory[chnid].phys_addr;
buf32.virt_addr =
(compat_ulong_t)
s_video_memory[chnid].virt_addr;
ret = copy_to_user((void __user *)arg,
&buf32, sizeof(struct compat_venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
up(&s_vpu_sem);
}
enc_pr(LOG_ALL,
"[-]IOC_GET_COMMON_BUF_INFO32\n");
}
break;
#endif
case IOC_WAIT_INTERRUPT:
{
struct venc_drv_intr_info_t info;
enc_pr(LOG_ALL, "[+]IOC_WAIT_INTERRUPT\n");
ret = copy_from_user(&info, (struct venc_drv_intr_info_t *)arg, sizeof(struct venc_drv_intr_info_t));
if (ret != 0)
{
return -EFAULT;
}
ret = wait_event_interruptible_timeout(s_interrupt_wait_q, s_interrupt_flag != 0, msecs_to_jiffies(info.timeout));
info.intr_reason = dev->interrupt_reason;
s_interrupt_flag = 0;
dev->interrupt_reason = 0;
ret = copy_to_user((void __user *)arg, &info, sizeof(struct venc_drv_intr_info_t));
if (ret != 0)
{
return -EFAULT;
}
enc_pr(LOG_ALL, "[-]IOC_WAIT_INTERRUPT\n");
}
break;
case IOC_GET_FW_BUF_INFO:
{
enc_pr(LOG_ALL,
"[+]IOC_GET_FW_BUF_INFO\n");
if (fw_buffer.phys_addr != 0) {
ret = copy_to_user((void __user *)arg,
&fw_buffer,
sizeof(struct venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
} else {
ret = down_interruptible(&s_vpu_sem);
if (ret != 0)
break;
fw_buffer.size = MC_SIZE;
fw_buffer.phys_addr = (ulong)codec_mm_alloc_for_dma(DEVICE_NAME, PAGE_ALIGN(fw_buffer.size) >> PAGE_SHIFT, 0, 0);
if (fw_buffer.phys_addr) {
fw_buffer.base = fw_buffer.phys_addr;
enc_pr(LOG_DEBUG, "allocating phys 0x%lx, ", fw_buffer.phys_addr);
enc_pr(LOG_DEBUG, "virt addr 0x%lx, size %dk\n", fw_buffer.virt_addr, fw_buffer.size >> 10);
} else {
enc_pr(LOG_ERROR, "Failed to alloc dma buffer %s, phys:0x%lx\n", DEVICE_NAME, fw_buffer.phys_addr);
if (fw_buffer.phys_addr)
codec_mm_free_for_dma(DEVICE_NAME, fw_buffer.phys_addr);
fw_buffer.phys_addr = 0;
ret = -ENOMEM;
up(&s_vpu_sem);
break;
}
ret = copy_to_user((void __user *)arg,
&fw_buffer,
sizeof(struct venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
up(&s_vpu_sem);
}
enc_pr(LOG_ALL,
"[-]IOC_GET_FW_BUF_INFO\n");
}
break;
#ifdef CONFIG_COMPAT
case IOC_GET_FW_BUF_INFO32:
{
struct compat_venc_drv_buffer_t buf32;
enc_pr(LOG_ALL,
"[+]IOC_GET_FW_BUF_INFO32\n");
memset(&buf32, 0, sizeof(struct compat_venc_drv_buffer_t));
buf32.size = fw_buffer.size;
buf32.phys_addr =
(compat_ulong_t)
fw_buffer.phys_addr;
buf32.virt_addr =
(compat_ulong_t)
fw_buffer.virt_addr;
if (fw_buffer.phys_addr != 0) {
ret = copy_to_user((void __user *)arg,
&buf32,
sizeof(struct compat_venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
} else {
ret = down_interruptible(&s_vpu_sem);
if (ret != 0)
break;
fw_buffer.size = MC_SIZE;
fw_buffer.phys_addr = (ulong)codec_mm_alloc_for_dma(DEVICE_NAME, fw_buffer.size >> PAGE_SHIFT, 0, 0);
if (fw_buffer.phys_addr) {
fw_buffer.base = fw_buffer.phys_addr;
enc_pr(LOG_DEBUG, "allocating phys 0x%lx, ", fw_buffer.phys_addr);
enc_pr(LOG_DEBUG, "virt addr 0x%lx, size %dk\n", fw_buffer.virt_addr, fw_buffer.size >> 10);
} else {
enc_pr(LOG_ERROR, "Failed to alloc dma buffer %s, phys:0x%lx\n", DEVICE_NAME, fw_buffer.phys_addr);
if (fw_buffer.phys_addr)
codec_mm_free_for_dma(DEVICE_NAME, fw_buffer.phys_addr);
fw_buffer.phys_addr = 0;
ret = -ENOMEM;
up(&s_vpu_sem);
break;
}
buf32.size =
fw_buffer.size;
buf32.phys_addr =
(compat_ulong_t)
fw_buffer.phys_addr;
buf32.virt_addr =
(compat_ulong_t)
fw_buffer.virt_addr;
ret = copy_to_user((void __user *)arg,
&buf32, sizeof(struct compat_venc_drv_buffer_t));
if (ret != 0)
ret = -EFAULT;
up(&s_vpu_sem);
}
enc_pr(LOG_ALL,
"[-]IOC_GET_FW_BUF_INFO32\n");
}
break;
#endif
case IOC_ALLOCATE_PHYSICAL_MEMORY:
{
struct venc_drv_buffer_pool_t *vbp;
enc_pr(LOG_ALL,
"[+]IOC_ALLOCATE_PHYSICAL_MEMORY\n");
ret = down_interruptible(&s_vpu_sem);
if (ret == 0) {
vbp = kzalloc(sizeof(*vbp), GFP_KERNEL);
if (!vbp) {
up(&s_vpu_sem);
return -ENOMEM;
}
ret = copy_from_user(&(vbp->vb),
(struct venc_drv_buffer_t *)arg,
sizeof(struct venc_drv_buffer_t));
if (ret) {
kfree(vbp);
up(&s_vpu_sem);
return -EFAULT;
}
ret = tahoe_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(struct venc_drv_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);
}
enc_pr(LOG_ALL,
"[-]IOC_ALLOCATE_PHYSICAL_MEMORY\n");
}
break;
#ifdef CONFIG_COMPAT
case IOC_ALLOCATE_PHYSICAL_MEMORY32:
{
struct venc_drv_buffer_pool_t *vbp;
struct compat_venc_drv_buffer_t buf32;
enc_pr(LOG_ALL,
"[+]IOC_ALLOCATE_PHYSICAL_MEMORY32\n");
ret = down_interruptible(&s_vpu_sem);
if (ret == 0) {
vbp = kzalloc(sizeof(*vbp), GFP_KERNEL);
if (!vbp) {
up(&s_vpu_sem);
return -ENOMEM;
}
ret = copy_from_user(&buf32,
(struct compat_venc_drv_buffer_t *)arg,
sizeof(struct compat_venc_drv_buffer_t));
if (ret) {
kfree(vbp);
up(&s_vpu_sem);
return -EFAULT;
}
vbp->vb.size = buf32.size;
vbp->vb.cached = buf32.cached;
vbp->vb.phys_addr =
(ulong)buf32.phys_addr;
vbp->vb.virt_addr =
(ulong)buf32.virt_addr;
ret = tahoe_alloc_dma_buffer(&(vbp->vb));
if (ret == -1) {
ret = -ENOMEM;
kfree(vbp);
up(&s_vpu_sem);
break;
}
buf32.size = vbp->vb.size;
buf32.phys_addr =
(compat_ulong_t)vbp->vb.phys_addr;
buf32.virt_addr =
(compat_ulong_t)vbp->vb.virt_addr;
ret = copy_to_user((void __user *)arg,
&buf32,
sizeof(struct compat_venc_drv_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);
}
enc_pr(LOG_ALL,
"[-]IOC_ALLOCATE_PHYSICAL_MEMORY32\n");
}
break;
#endif
case IOC_FLUSH_BUFFER:
{
struct venc_drv_buffer_pool_t *pool, *n;
struct venc_drv_buffer_t vb, buf;
bool find = false;
u32 cached = 0;
enc_pr(LOG_ALL,
"[+]IOC_FLUSH_BUFFER\n");
ret = copy_from_user(&buf,
(struct venc_drv_buffer_t *)arg,
sizeof(struct venc_drv_buffer_t));
if (ret)
return -EFAULT;
spin_lock(&s_vpu_lock);
list_for_each_entry_safe(pool, n,
&s_vbp_head, list) {
if (pool->filp == filp) {
vb = pool->vb;
if ((vb.phys_addr <= buf.phys_addr)
&& ((vb.phys_addr + vb.size)
> buf.phys_addr)
&& find == false){
cached = vb.cached;
find = true;
break;
}
}
}
spin_unlock(&s_vpu_lock);
if (find && cached)
dma_flush(
(u32)buf.phys_addr,
(u32)buf.size);
enc_pr(LOG_ALL,
"[-]IOC_FLUSH_BUFFER\n");
}
break;
case IOC_CACHE_INV_BUFFER:
{
struct venc_drv_buffer_t buf;
struct venc_drv_buffer_pool_t *pool, *n;
struct venc_drv_buffer_t vb;
bool find = false;
u32 cached = 0;
enc_pr(LOG_ALL,
"[+]IOC_CACHE_INV_BUFFER\n");
ret = copy_from_user(&buf,
(struct venc_drv_buffer_t *)arg,
sizeof(struct venc_drv_buffer_t));
if (ret)
return -EFAULT;
spin_lock(&s_vpu_lock);
list_for_each_entry_safe(pool, n, &s_vbp_head, list) {
if (pool->filp == filp) {
vb = pool->vb;
if ((vb.phys_addr <= buf.phys_addr) && ((vb.phys_addr + vb.size) > buf.phys_addr) && find == false) {
cached = vb.cached;
find = true;
break;
}
}
}
spin_unlock(&s_vpu_lock);
if (find && cached) {
enc_pr(LOG_INFO, "[%d]doing cache flush for 0x%lx~0x%lx\n", __LINE__, (long)(buf.phys_addr), (long)(buf.phys_addr+buf.size));
cache_flush((u32)buf.phys_addr,(u32)buf.size);
}
enc_pr(LOG_ALL,"[-]IOC_CACHE_INV_BUFFER\n");
}
break;
#ifdef CONFIG_COMPAT
case IOC_FLUSH_BUFFER32:
{
struct venc_drv_buffer_pool_t *pool, *n;
struct compat_venc_drv_buffer_t buf32;
struct venc_drv_buffer_t vb;
bool find = false;
u32 cached = 0;
enc_pr(LOG_ALL,
"[+]IOC_FLUSH_BUFFER32\n");
ret = copy_from_user(&buf32,
(struct compat_venc_drv_buffer_t *)arg,
sizeof(struct compat_venc_drv_buffer_t));
if (ret)
return -EFAULT;
spin_lock(&s_vpu_lock);
list_for_each_entry_safe(pool, n,
&s_vbp_head, list) {
if (pool->filp == filp) {
vb = pool->vb;
if (((compat_ulong_t)vb.phys_addr <=
buf32.phys_addr) &&
(((compat_ulong_t)vb.phys_addr
+ vb.size)
> buf32.phys_addr)
&& find == false){
cached = vb.cached;
find = true;
break;
}
}
}
spin_unlock(&s_vpu_lock);
if (find && cached)
dma_flush(
(u32)buf32.phys_addr,
(u32)buf32.size);
enc_pr(LOG_ALL,
"[-]IOC_FLUSH_BUFFER32\n");
}
break;
case IOC_CACHE_INV_BUFFER32:
{
struct compat_venc_drv_buffer_t buf32;
struct venc_drv_buffer_pool_t *pool, *n;
struct venc_drv_buffer_t vb;
bool find = false;
u32 cached = 0;
enc_pr(LOG_ALL, "[+]IOC_CACHE_INV_BUFFER32\n");
ret = copy_from_user(&buf32,
(struct compat_venc_drv_buffer_t *)arg,
sizeof(struct compat_venc_drv_buffer_t));
if (ret)
return -EFAULT;
spin_lock(&s_vpu_lock);
list_for_each_entry_safe(pool, n,
&s_vbp_head, list) {
if (pool->filp == filp) {
vb = pool->vb;
if (((compat_ulong_t)vb.phys_addr
<= buf32.phys_addr)
&& (((compat_ulong_t)vb.phys_addr +
vb.size) > buf32.phys_addr)
&& find == false){
cached = vb.cached;
find = true;
break;
}
}
}
spin_unlock(&s_vpu_lock);
if (find && cached) {
cache_flush((u32)buf32.phys_addr, (u32)buf32.size);
/*if (dump_es) {
enc_pr(LOG_INFO, "dump es frame, size=%u\n", (u32)buf32.size);
dump_data((u32)buf32.phys_addr, (u32)buf32.size);
}*/
}
enc_pr(LOG_INFO, "[-]IOC_CACHE_INV_BUFFER32\n");
}
break;
#endif
case IOC_NR_FOR_DEBUG:
{
u32 data = 0;
enc_pr(LOG_ALL,
"[+]IOC_NR_FOR_DEBUG\n");
if (get_user(data, (u32 __user *) arg))
return -EFAULT;
enc_pr(LOG_INFO, "============add_for_debug data:%d\n", data);
enc_pr(LOG_ALL,"[-]IOC_NR_FOR_DEBUG\n");
}
break;
case IOC_CONFIG_DMA:
{
struct tahoe_dma_buf_info_t dma_info;
enc_pr(LOG_DEBUG,
"[+]IOC_CONFIG_DMA\n");
if (copy_from_user(&dma_info,
(struct tahoe_dma_buf_info_t *)arg,
sizeof(struct tahoe_dma_buf_info_t)))
return -EFAULT;
ret = down_interruptible(&s_vpu_sem);
if (ret != 0)
break;
if (tahoe_src_addr_config(&dma_info, filp)) {
up(&s_vpu_sem);
enc_pr(LOG_ERROR,
"src addr config error\n");
ret = -EFAULT;
break;
}
up(&s_vpu_sem);
ret = copy_to_user((void __user *)arg,
&dma_info,
sizeof(struct tahoe_dma_buf_info_t));
if (ret) {
ret = -EFAULT;
break;
}
enc_pr(LOG_DEBUG,
"[-]IOC_CONFIG_DMA %d, %d, %d\n",
dma_info.fd[0],
dma_info.fd[1],
dma_info.fd[2]);
}
break;
case IOC_UNMAP_DMA:
{
struct tahoe_dma_buf_info_t dma_info;
enc_pr(LOG_ALL,
"[+]IOC_UNMAP_DMA\n");
if (copy_from_user(&dma_info,
(struct tahoe_dma_buf_info_t *)arg,
sizeof(struct tahoe_dma_buf_info_t)))
return -EFAULT;
ret = down_interruptible(&s_vpu_sem);
if (ret != 0)
break;
if (tahoe_src_addr_unmap(&dma_info, filp)) {
up(&s_vpu_sem);
enc_pr(LOG_ERROR,
"dma addr unmap config error\n");
ret = -EFAULT;
break;
}
up(&s_vpu_sem);
enc_pr(LOG_ALL,
"[-]VDI_IOCTL_UNMAP_DMA\n");
}
break;
default:
return -EINVAL;
}
enc_pr(LOG_ALL, "tahoe_ioctl end\n");
return ret;
}
#ifdef CONFIG_COMPAT
static long tahoe_compat_ioctl(struct file *filp, u32 cmd, ulong arg)
{
long ret;
arg = (ulong)compat_ptr(arg);
ret = tahoe_ioctl(filp, cmd, arg);
return ret;
}
#endif
static u32 tahoe_poll(struct file *file, poll_table *wait_table)
{
struct venc_drv_context_t *s_context = (struct venc_drv_context_t *)file->private_data;;
poll_wait(file, &s_context->request_complete, wait_table);
if (atomic_read(&s_context->request_ready)) {
atomic_dec(&s_context->request_ready);
return POLLIN | POLLRDNORM;
}
return 0;
}
static const struct file_operations tahoe_fops = {
.owner = THIS_MODULE,
.open = tahoe_open,
.release = tahoe_release,
.unlocked_ioctl = tahoe_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tahoe_compat_ioctl,
#endif
.mmap = tahoe_mmap,
.poll = tahoe_poll,
};
static ssize_t chip_id_show(KV_CLASS_CONST struct class *cla,
KV_CLASS_ATTR_CONST struct class_attribute *attr, char *buf)
{
//char *pbuf = buf;
//pbuf += sprintf(pbuf, "Chip ID: 0x%08x\n", ReadTahoeRegister(CHIP_ID));
//return pbuf - buf;
return 0;
}
static CLASS_ATTR_RO(chip_id);
static struct attribute *tahoe_class_attrs[] = {
&class_attr_chip_id.attr,
NULL
};
ATTRIBUTE_GROUPS(tahoe_class);
static struct class tahoe_class = {
.name = CLASS_NAME,
.class_groups = tahoe_class_groups,
};
s32 init_tahoe_device(void)
{
s32 r = 0;
r = register_chrdev(0, DEVICE_NAME, &tahoe_fops);
if (r <= 0) {
enc_pr(LOG_ERROR, "register tahoe device error.\n");
return r;
}
s_tahoe_major = r;
r = class_register(&tahoe_class);
if (r < 0) {
enc_pr(LOG_ERROR, "error create tahoe class.\n");
return r;
}
s_register_flag = 1;
tahoe_dev = device_create(&tahoe_class, NULL,
MKDEV(s_tahoe_major, 0), NULL,
DEVICE_NAME);
if (IS_ERR(tahoe_dev)) {
enc_pr(LOG_ERROR, "create create device error.\n");
class_unregister(&tahoe_class);
return -1;
}
return r;
}
s32 uninit_tahoe_device(void)
{
if (tahoe_dev)
device_destroy(&tahoe_class, MKDEV(s_tahoe_major, 0));
if (s_register_flag)
class_destroy(&tahoe_class);
s_register_flag = 0;
if (s_tahoe_major)
unregister_chrdev(s_tahoe_major, DEVICE_NAME);
s_tahoe_major = 0;
return 0;
}
static void tahoe_hw_reset(void)
{
if (IS_ERR(s_tahoe_rsts.core_rst) ||
IS_ERR(s_tahoe_rsts.a_rst) ||
IS_ERR(s_tahoe_rsts.apb_rst) ||
IS_ERR(s_tahoe_rsts.sys_rst)) {
return;
}
reset_control_reset(s_tahoe_rsts.core_rst);
reset_control_reset(s_tahoe_rsts.a_rst);
reset_control_reset(s_tahoe_rsts.apb_rst);
reset_control_reset(s_tahoe_rsts.sys_rst);
}
static void tahoe_clk_put(struct device *dev, struct tahoe_clks *clks)
{
if (!(clks->sys_tahoe == NULL || IS_ERR(clks->sys_tahoe)))
devm_clk_put(dev, clks->sys_tahoe);
if (!(clks->tahoe_clk == NULL || IS_ERR(clks->tahoe_clk)))
devm_clk_put(dev, clks->tahoe_clk);
}
static int tahoe_clk_get(struct device *dev, struct tahoe_clks *clks)
{
int ret = 0;
clks->sys_tahoe = devm_clk_get(dev, "sys_tahoe");
if (IS_ERR(clks->sys_tahoe)) {
enc_pr(LOG_ERROR, "cannot get sys_tahoe clock\n");
clks->sys_tahoe = NULL;
//ret = -ENOENT;
//goto err;
}
clks->tahoe_clk = devm_clk_get(dev, "clk_tahoe");
if (IS_ERR(clks->tahoe_clk)) {
enc_pr(LOG_ERROR, "cannot get clk_tahoe clock\n");
clks->tahoe_clk = NULL;
ret = -ENOENT;
goto err;
}
return 0;
err:
tahoe_clk_put(dev, clks);
return ret;
}
static int tahoe_rst_get(struct device *dev, struct tahoe_rsts *rsts)
{
//int ret = 0;
rsts->core_rst = devm_reset_control_get(dev, "tahoe_core_rst");
if (IS_ERR(rsts->core_rst)) {
enc_pr(LOG_ERROR, "cannot get core_rst\n");
rsts->core_rst = NULL;
//ret = -ENOENT;
//goto err;
}
rsts->a_rst = devm_reset_control_get(dev, "tahoe_a_rst");
if (IS_ERR(rsts->a_rst)) {
enc_pr(LOG_ERROR, "cannot get a_rst\n");
rsts->a_rst = NULL;
//ret = -ENOENT;
//goto err;
}
rsts->apb_rst = devm_reset_control_get(dev, "tahoe_apb_rst");
if (IS_ERR(rsts->apb_rst)) {
enc_pr(LOG_ERROR, "cannot get apb_rst\n");
rsts->apb_rst = NULL;
//ret = -ENOENT;
//goto err;
}
rsts->sys_rst = devm_reset_control_get(dev, "tahoe_sys_rst");
if (IS_ERR(rsts->sys_rst)) {
enc_pr(LOG_ERROR, "cannot get sys_rst\n");
rsts->sys_rst = NULL;
//ret = -ENOENT;
//goto err;
}
return 0;
//err:
//return ret;
}
static void tahoe_clk_enable(struct tahoe_clks *clks)
{
if (clks->sys_tahoe != NULL) {
clk_prepare_enable(clks->sys_tahoe);
}
if (clock_a > 0) {
enc_pr(LOG_INFO, "tahoe: desired clock_a freq %u\n", clock_a);
clk_set_rate(clks->tahoe_clk, clock_a);
} else
clk_set_rate(clks->tahoe_clk, 666666666);
clk_prepare_enable(clks->tahoe_clk);
enc_pr(LOG_DEBUG, "tahoe: %ld\n", clk_get_rate(clks->tahoe_clk));
/* the power on*/
pm_runtime_get_sync(&tahoe_pdev->dev);
mdelay(5);
enc_pr(LOG_INFO, "tahoe power stauts after poweron: %d\n", pm_runtime_active(&tahoe_pdev->dev));
/* reset */
tahoe_hw_reset(); //__need_modify__
/* gate the clocks */
#ifdef TAHOE_SUPPORT_CLOCK_CONTROL
enc_pr(LOG_INFO, "tahoe_clk_enable, now gate off the clock\n");
clk_disable(clks->tahoe_clk);
#endif
}
static void tahoe_clk_disable(struct tahoe_clks *clks)
{
#ifdef TAHOE_SUPPORT_CLOCK_CONTROL
if (clock_gate_count > 0)
#endif
{
enc_pr(LOG_INFO, "tahoe unclosed clock %d\n", clock_gate_count);
clk_disable(clks->tahoe_clk);
clock_gate_count = 0;
}
clk_unprepare(clks->tahoe_clk);
if (clks->sys_tahoe != NULL) {
clk_disable_unprepare(clks->sys_tahoe);
}
/* the power off */
pm_runtime_put_sync(&tahoe_pdev->dev);
mdelay(5);
enc_pr(LOG_INFO, "tahoe power stauts after poweroff: %d\n", pm_runtime_active(&tahoe_pdev->dev));
}
static s32 tahoe_probe(struct platform_device *pdev)
{
s32 err = 0, irq, reg_count;
struct resource res;
struct device_node *np, *child;
enc_pr(LOG_DEBUG, "tahoe_probe\n");
s_tahoe_major = 0;
use_reserve = false;
s_tahoe_irq = -1;
s_tahoe_irq_requested = false;
spin_lock(&s_vpu_lock);
s_tahoe_open_ref_count = 0;
spin_unlock(&s_vpu_lock);
tahoe_dev = NULL;
tahoe_pdev = NULL;
s_register_flag = 0;
memset(s_video_memory, 0, sizeof(struct venc_drv_common_buffer_t));
memset(&s_tahoe_register, 0, sizeof(struct venc_drv_buffer_t));
memset(&s_bit_firmware_info, 0, sizeof(s_bit_firmware_info));
memset(&res, 0, sizeof(struct resource));
cma_cfg_size = 36*SZ_1M; //__need_modify__
s_tahoe_register.phys_addr = TAHOE_REG_BASE_ADDR;
s_tahoe_register.virt_addr = (unsigned long)ioremap(s_tahoe_register.phys_addr, TAHOE_REG_SIZE);
s_tahoe_register.size = TAHOE_REG_SIZE;
enc_pr(LOG_ERROR, "s_venc_register.phys_addr = 0x%lx\n", s_tahoe_register.phys_addr);
enc_pr(LOG_ERROR, "s_venc_register.virt_addr = 0x%lx, size = 0x%x\n", s_tahoe_register.virt_addr, TAHOE_REG_SIZE);
/* get interrupt resource */
irq = platform_get_irq_byname(pdev, "tahoe_irq0");
if (irq < 0) {
enc_pr(LOG_ERROR, "get tahoe irq resource error\n");
err = -EFAULT;
goto ERROR_PROVE_DEVICE;
}
s_tahoe_irq = irq;
enc_pr(LOG_DEBUG, "tahoe -irq: %d\n", s_tahoe_irq);
/* get vpu clks */
if (tahoe_clk_get(&pdev->dev, &s_tahoe_clks)) {
enc_pr(LOG_DEBUG, "get tahoe clks fail.\n");
goto ERROR_PROVE_DEVICE;
} else
enc_pr(LOG_DEBUG, "tahoe. clock get success\n");
reg_count = 0;
np = pdev->dev.of_node;
for_each_child_of_node(np, child) {
if (of_address_to_resource(child, 0, &res) || (reg_count > 1)) {
enc_pr(LOG_ERROR, "no reg ranges or more reg ranges %d\n", reg_count);
err = -ENXIO;
goto ERROR_PROVE_DEVICE;
}
/* if platform driver is implemented */
if (res.start != 0) {
s_tahoe_register.phys_addr = res.start;
s_tahoe_register.virt_addr = (ulong)ioremap(res.start, resource_size(&res));
s_tahoe_register.size = res.end - res.start;
enc_pr(LOG_DEBUG, "tahoe base address get from platform driver ");
enc_pr(LOG_DEBUG, "physical addr=0x%lx, virtual addr=0x%lx\n",
s_tahoe_register.phys_addr, s_tahoe_register.virt_addr);
} else {
s_tahoe_register.phys_addr = TAHOE_REG_BASE_ADDR;
s_tahoe_register.virt_addr = (ulong)ioremap(s_tahoe_register.phys_addr, TAHOE_REG_SIZE);
s_tahoe_register.size = TAHOE_REG_SIZE;
enc_pr(LOG_DEBUG, "tahoe base address get from defined value ");
enc_pr(LOG_DEBUG, "physical addr=0x%lx, virtual addr=0x%lx\n",
s_tahoe_register.phys_addr, s_tahoe_register.virt_addr);
}
reg_count++;
}
/* reset: tahoe_core_rst/tahoe_a_rst/tahoe_apb_rst/tahoe_sys_rst */
if (tahoe_rst_get(&pdev->dev, &s_tahoe_rsts)) {
enc_pr(LOG_DEBUG, "get tahoe rsts fail.\n");
goto ERROR_PROVE_DEVICE;
} else
enc_pr(LOG_DEBUG, "tahoe. rsts get success\n");
/* get the major number of the character device */
if (init_tahoe_device()) {
err = -EBUSY;
enc_pr(LOG_ERROR, "could not allocate major number\n");
goto ERROR_PROVE_DEVICE;
}
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
enc_pr(LOG_INFO, "SUCCESS alloc_chrdev_region\n");
tasklet_init(&tahoe_tasklet, tahoe_isr_tasklet, (ulong)&s_tahoe_drv_context);
tahoe_pdev = pdev;
pm_runtime_enable(&tahoe_pdev->dev);
return 0;
ERROR_PROVE_DEVICE:
if (s_tahoe_register.virt_addr) {
iounmap((void *)s_tahoe_register.virt_addr);
memset(&s_tahoe_register, 0, sizeof(struct venc_drv_buffer_t));
}
uninit_tahoe_device();
return err;
}
static KV_INT_TO_VOID tahoe_remove(struct platform_device *pdev)
{
int i;
enc_pr(LOG_DEBUG, "tahoe_remove\n");
for (i = 0; i < MAX_INSTANCE; i++) {
if (s_video_memory[i].phys_addr) {
codec_mm_free_for_dma(
DEVICE_NAME,
s_video_memory[i].phys_addr);
memset(&s_video_memory[i],
0, sizeof(struct venc_drv_common_buffer_t));
}
}
if (s_tahoe_irq_requested == true) {
if (s_tahoe_irq >= 0) {
free_irq(s_tahoe_irq, &s_tahoe_drv_context);
s_tahoe_irq = -1;
}
s_tahoe_irq_requested = false;
}
if (s_tahoe_register.virt_addr) {
iounmap((void *)s_tahoe_register.virt_addr);
memset(&s_tahoe_register,
0, sizeof(struct venc_drv_buffer_t));
}
if (clock_gate_count > 0)
{
tahoe_clk_disable(&s_tahoe_clks);
}
tahoe_clk_put(&tahoe_pdev->dev, &s_tahoe_clks);
tahoe_pdev = NULL;
uninit_tahoe_device();
return KV_RET_x_TO_VOID(0);
}
static const struct of_device_id tahoe_dt_match[] = {
{
.compatible = "amlogic, tahoe",
},
{},
};
static struct platform_driver tahoe_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = tahoe_dt_match,
},
.probe = tahoe_probe,
.remove = tahoe_remove,
};
static s32 __init tahoe_init(void)
{
s32 res;
enc_pr(LOG_DEBUG, "tahoe_init\n");
res = platform_driver_register(&tahoe_driver);
enc_pr(LOG_DEBUG, "tahoe_init result = 0x%x\n", res);
return res;
}
static void __exit tahoe_exit(void)
{
enc_pr(LOG_DEBUG, "tahoe_exit\n");
platform_driver_unregister(&tahoe_driver);
}
module_param(print_level, uint, 0664);
MODULE_PARM_DESC(print_level, "\n print_level\n");
module_param(clock_a, uint, 0664);
MODULE_PARM_DESC(clock_a, "\n clock_a\n");
MODULE_AUTHOR("Amlogic Inc.");
MODULE_DESCRIPTION("Tahoe linux driver");
MODULE_LICENSE("GPL");
module_init(tahoe_init);
module_exit(tahoe_exit);