/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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);