/* Copyright (c) 2010-2013, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vidc_init_internal.h" #include "vcd_res_tracker_api.h" #if DEBUG #define DBG(x...) printk(KERN_DEBUG x) #else #define DBG(x...) #endif #define VIDC_NAME "msm_vidc_reg" #define ERR(x...) printk(KERN_ERR x) static struct vidc_dev *vidc_device_p; static dev_t vidc_dev_num; static struct class *vidc_class; static const struct file_operations vidc_fops = { .owner = THIS_MODULE, .open = NULL, .release = NULL, .unlocked_ioctl = NULL, }; struct workqueue_struct *vidc_wq; struct workqueue_struct *vidc_timer_wq; static irqreturn_t vidc_isr(int irq, void *dev); static spinlock_t vidc_spin_lock; u32 vidc_msg_timing, vidc_msg_pmem, vidc_msg_register; #ifdef VIDC_ENABLE_DBGFS struct dentry *vidc_debugfs_root; struct dentry *vidc_get_debugfs_root(void) { if (vidc_debugfs_root == NULL) vidc_debugfs_root = debugfs_create_dir("vidc", NULL); return vidc_debugfs_root; } void vidc_debugfs_file_create(struct dentry *root, const char *name, u32 *var) { struct dentry *vidc_debugfs_file = debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var); if (!vidc_debugfs_file) ERR("%s(): Error creating/opening file %s\n", __func__, name); } #endif static void vidc_timer_fn(unsigned long data) { unsigned long flag; struct vidc_timer *hw_timer = NULL; ERR("%s() Timer expired\n", __func__); spin_lock_irqsave(&vidc_spin_lock, flag); hw_timer = (struct vidc_timer *)data; list_add_tail(&hw_timer->list, &vidc_device_p->vidc_timer_queue); spin_unlock_irqrestore(&vidc_spin_lock, flag); DBG("Queue the work for timer\n"); queue_work(vidc_timer_wq, &vidc_device_p->vidc_timer_worker); } static void vidc_timer_handler(struct work_struct *work) { unsigned long flag = 0; u32 islist_empty = 0; struct vidc_timer *hw_timer = NULL; ERR("%s() Timer expired\n", __func__); do { spin_lock_irqsave(&vidc_spin_lock, flag); islist_empty = list_empty(&vidc_device_p->vidc_timer_queue); if (!islist_empty) { hw_timer = list_first_entry( &vidc_device_p->vidc_timer_queue, struct vidc_timer, list); list_del(&hw_timer->list); } spin_unlock_irqrestore(&vidc_spin_lock, flag); if (!islist_empty && hw_timer && hw_timer->cb_func) hw_timer->cb_func(hw_timer->userdata); } while (!islist_empty); } static void vidc_work_handler(struct work_struct *work) { DBG("vidc_work_handler()"); vcd_read_and_clear_interrupt(); vcd_response_handler(); enable_irq(vidc_device_p->irq); DBG("vidc_work_handler() done"); } static DECLARE_WORK(vidc_work, vidc_work_handler); static int __devinit vidc_720p_probe(struct platform_device *pdev) { struct resource *resource; DBG("Enter %s()\n", __func__); if (pdev->id) { ERR("Invalid plaform device ID = %d\n", pdev->id); return -EINVAL; } vidc_device_p->irq = platform_get_irq(pdev, 0); if (unlikely(vidc_device_p->irq < 0)) { ERR("%s(): Invalid irq = %d\n", __func__, vidc_device_p->irq); return -ENXIO; } resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) { ERR("%s(): Invalid resource\n", __func__); return -ENXIO; } vidc_device_p->phys_base = resource->start; vidc_device_p->virt_base = ioremap(resource->start, resource->end - resource->start + 1); if (!vidc_device_p->virt_base) { ERR("%s() : ioremap failed\n", __func__); return -ENOMEM; } vidc_device_p->device = &pdev->dev; mutex_init(&vidc_device_p->lock); vidc_wq = create_singlethread_workqueue("vidc_worker_queue"); if (!vidc_wq) { ERR("%s: create workque failed\n", __func__); return -ENOMEM; } pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); return 0; } static int __devexit vidc_720p_remove(struct platform_device *pdev) { if (pdev->id) { ERR("Invalid plaform device ID = %d\n", pdev->id); return -EINVAL; } pm_runtime_disable(&pdev->dev); return 0; } static int vidc_runtime_suspend(struct device *dev) { dev_dbg(dev, "pm_runtime: suspending...\n"); return 0; } static int vidc_runtime_resume(struct device *dev) { dev_dbg(dev, "pm_runtime: resuming...\n"); return 0; } static const struct dev_pm_ops vidc_dev_pm_ops = { .runtime_suspend = vidc_runtime_suspend, .runtime_resume = vidc_runtime_resume, }; static struct platform_driver msm_vidc_720p_platform_driver = { .probe = vidc_720p_probe, .remove = vidc_720p_remove, .driver = { .name = "msm_vidc", .pm = &vidc_dev_pm_ops, }, }; static void __exit vidc_exit(void) { platform_driver_unregister(&msm_vidc_720p_platform_driver); } static irqreturn_t vidc_isr(int irq, void *dev) { DBG("\n vidc_isr() %d ", irq); disable_irq_nosync(irq); queue_work(vidc_wq, &vidc_work); return IRQ_HANDLED; } static int __init vidc_init(void) { int rc = 0; struct device *class_devp; #ifdef VIDC_ENABLE_DBGFS struct dentry *root = NULL; #endif vidc_device_p = kzalloc(sizeof(struct vidc_dev), GFP_KERNEL); if (!vidc_device_p) { ERR("%s Unable to allocate memory for vidc_dev\n", __func__); return -ENOMEM; } rc = alloc_chrdev_region(&vidc_dev_num, 0, 1, VIDC_NAME); if (rc < 0) { ERR("%s: alloc_chrdev_region Failed rc = %d\n", __func__, rc); goto error_vidc_alloc_chrdev_region; } vidc_class = class_create(THIS_MODULE, VIDC_NAME); if (IS_ERR(vidc_class)) { rc = PTR_ERR(vidc_class); ERR("%s: couldn't create vidc_class rc = %d\n", __func__, rc); goto error_vidc_class_create; } class_devp = device_create(vidc_class, NULL, vidc_dev_num, NULL, VIDC_NAME); if (IS_ERR(class_devp)) { rc = PTR_ERR(class_devp); ERR("%s: class device_create failed %d\n", __func__, rc); goto error_vidc_class_device_create; } cdev_init(&vidc_device_p->cdev, &vidc_fops); vidc_device_p->cdev.owner = THIS_MODULE; rc = cdev_add(&(vidc_device_p->cdev), vidc_dev_num, 1); if (rc < 0) { ERR("%s: cdev_add failed %d\n", __func__, rc); goto error_vidc_cdev_add; } rc = platform_driver_register(&msm_vidc_720p_platform_driver); if (rc) { ERR("%s failed to load\n", __func__); goto error_vidc_platfom_register; } rc = request_irq(vidc_device_p->irq, vidc_isr, IRQF_TRIGGER_HIGH, "vidc", vidc_device_p->device); if (unlikely(rc)) { ERR("%s() :request_irq failed\n", __func__); goto error_vidc_request_irq; } res_trk_init(vidc_device_p->device, vidc_device_p->irq); vidc_timer_wq = create_singlethread_workqueue("vidc_timer_wq"); if (!vidc_timer_wq) { ERR("%s: create workque failed\n", __func__); rc = -ENOMEM; goto error_vidc_create_workqueue; } DBG("Disabling IRQ in %s()\n", __func__); disable_irq_nosync(vidc_device_p->irq); INIT_WORK(&vidc_device_p->vidc_timer_worker, vidc_timer_handler); spin_lock_init(&vidc_spin_lock); INIT_LIST_HEAD(&vidc_device_p->vidc_timer_queue); vidc_device_p->ref_count = 0; vidc_device_p->firmware_refcount = 0; vidc_device_p->get_firmware = 0; #ifdef VIDC_ENABLE_DBGFS root = vidc_get_debugfs_root(); if (root) { vidc_debugfs_file_create(root, "vidc_msg_timing", (u32 *) &vidc_msg_timing); vidc_debugfs_file_create(root, "vidc_msg_pmem", (u32 *) &vidc_msg_pmem); vidc_debugfs_file_create(root, "vidc_msg_register", (u32 *) &vidc_msg_register); } #endif return 0; error_vidc_create_workqueue: free_irq(vidc_device_p->irq, vidc_device_p->device); error_vidc_request_irq: platform_driver_unregister(&msm_vidc_720p_platform_driver); error_vidc_platfom_register: cdev_del(&(vidc_device_p->cdev)); error_vidc_cdev_add: device_destroy(vidc_class, vidc_dev_num); error_vidc_class_device_create: class_destroy(vidc_class); error_vidc_class_create: unregister_chrdev_region(vidc_dev_num, 1); error_vidc_alloc_chrdev_region: kfree(vidc_device_p); return rc; } void __iomem *vidc_get_ioaddr(void) { return (u8 *)vidc_device_p->virt_base; } EXPORT_SYMBOL(vidc_get_ioaddr); int vidc_load_firmware(void) { u32 status = true; if (!res_trk_check_for_sec_session()) { mutex_lock(&vidc_device_p->lock); if (!vidc_device_p->get_firmware) { status = res_trk_download_firmware(); if (!status) goto error; vidc_device_p->get_firmware = 1; } vidc_device_p->firmware_refcount++; error: mutex_unlock(&vidc_device_p->lock); } return status; } EXPORT_SYMBOL(vidc_load_firmware); void vidc_release_firmware(void) { if (!res_trk_check_for_sec_session()) { mutex_lock(&vidc_device_p->lock); if (vidc_device_p->firmware_refcount > 0) vidc_device_p->firmware_refcount--; else vidc_device_p->firmware_refcount = 0; mutex_unlock(&vidc_device_p->lock); } } EXPORT_SYMBOL(vidc_release_firmware); u32 vidc_get_fd_info(struct video_client_ctx *client_ctx, enum buffer_dir buffer, int pmem_fd, unsigned long kvaddr, int index, struct ion_handle **buff_handle) { struct buf_addr_table *buf_addr_table; u32 rc = 0; if (!client_ctx) return false; if (buffer == BUFFER_TYPE_INPUT) buf_addr_table = client_ctx->input_buf_addr_table; else buf_addr_table = client_ctx->output_buf_addr_table; if (buf_addr_table[index].pmem_fd == pmem_fd) { if (buf_addr_table[index].kernel_vaddr == kvaddr) { rc = buf_addr_table[index].buff_ion_flag; *buff_handle = buf_addr_table[index].buff_ion_handle; } else *buff_handle = NULL; } else *buff_handle = NULL; return rc; } EXPORT_SYMBOL(vidc_get_fd_info); void vidc_cleanup_addr_table(struct video_client_ctx *client_ctx, enum buffer_dir buffer) { u32 *num_of_buffers = NULL; u32 i = 0; struct buf_addr_table *buf_addr_table; if (buffer == BUFFER_TYPE_INPUT) { buf_addr_table = client_ctx->input_buf_addr_table; num_of_buffers = &client_ctx->num_of_input_buffers; DBG("%s(): buffer = INPUT\n", __func__); } else { buf_addr_table = client_ctx->output_buf_addr_table; num_of_buffers = &client_ctx->num_of_output_buffers; DBG("%s(): buffer = OUTPUT\n", __func__); } if (!*num_of_buffers) goto bail_out_cleanup; if (!client_ctx->user_ion_client) goto bail_out_cleanup; for (i = 0; i < *num_of_buffers; ++i) { if (!IS_ERR_OR_NULL(buf_addr_table[i].buff_ion_handle)) { if (!IS_ERR_OR_NULL(client_ctx->user_ion_client)) { ion_unmap_kernel(client_ctx->user_ion_client, buf_addr_table[i]. buff_ion_handle); if (!res_trk_check_for_sec_session() && (res_trk_get_core_type() != (u32)VCD_CORE_720P)) { ion_unmap_iommu( client_ctx->user_ion_client, buf_addr_table[i]. buff_ion_handle, VIDEO_DOMAIN, VIDEO_MAIN_POOL); } ion_free(client_ctx->user_ion_client, buf_addr_table[i]. buff_ion_handle); buf_addr_table[i].buff_ion_handle = NULL; } } } if (!IS_ERR_OR_NULL(client_ctx->h264_mv_ion_handle)) { if (!IS_ERR_OR_NULL(client_ctx->user_ion_client)) { ion_unmap_kernel(client_ctx->user_ion_client, client_ctx->h264_mv_ion_handle); if (!res_trk_check_for_sec_session() && (res_trk_get_core_type() != (u32)VCD_CORE_720P)) { ion_unmap_iommu(client_ctx->user_ion_client, client_ctx->h264_mv_ion_handle, VIDEO_DOMAIN, VIDEO_MAIN_POOL); } ion_free(client_ctx->user_ion_client, client_ctx->h264_mv_ion_handle); client_ctx->h264_mv_ion_handle = NULL; } } bail_out_cleanup: return; } EXPORT_SYMBOL(vidc_cleanup_addr_table); u32 vidc_lookup_addr_table(struct video_client_ctx *client_ctx, enum buffer_dir buffer, u32 search_with_user_vaddr, unsigned long *user_vaddr, unsigned long *kernel_vaddr, unsigned long *phy_addr, int *pmem_fd, struct file **file, s32 *buffer_index) { u32 num_of_buffers; u32 i; struct buf_addr_table *buf_addr_table; u32 found = false; if (!client_ctx) return false; mutex_lock(&client_ctx->enrty_queue_lock); if (buffer == BUFFER_TYPE_INPUT) { buf_addr_table = client_ctx->input_buf_addr_table; num_of_buffers = client_ctx->num_of_input_buffers; DBG("%s(): buffer = INPUT\n", __func__); } else { buf_addr_table = client_ctx->output_buf_addr_table; num_of_buffers = client_ctx->num_of_output_buffers; DBG("%s(): buffer = OUTPUT\n", __func__); } for (i = 0; i < num_of_buffers; ++i) { if (search_with_user_vaddr) { if (*user_vaddr == buf_addr_table[i].user_vaddr) { *kernel_vaddr = buf_addr_table[i].kernel_vaddr; found = true; DBG("%s() : client_ctx = %p." " user_virt_addr = 0x%08lx is found", __func__, client_ctx, *user_vaddr); break; } } else { if (*kernel_vaddr == buf_addr_table[i].kernel_vaddr) { *user_vaddr = buf_addr_table[i].user_vaddr; found = true; DBG("%s() : client_ctx = %p." " kernel_virt_addr = 0x%08lx is found", __func__, client_ctx, *kernel_vaddr); break; } } } if (found) { *phy_addr = buf_addr_table[i].dev_addr; *pmem_fd = buf_addr_table[i].pmem_fd; *file = buf_addr_table[i].file; *buffer_index = i; if (search_with_user_vaddr) DBG("kernel_vaddr = 0x%08lx, phy_addr = 0x%08lx " " pmem_fd = %d, struct *file = %p " "buffer_index = %d\n", *kernel_vaddr, *phy_addr, *pmem_fd, *file, *buffer_index); else DBG("user_vaddr = 0x%08lx, phy_addr = 0x%08lx " " pmem_fd = %d, struct *file = %p " "buffer_index = %d\n", *user_vaddr, *phy_addr, *pmem_fd, *file, *buffer_index); mutex_unlock(&client_ctx->enrty_queue_lock); return true; } else { if (search_with_user_vaddr) DBG("%s() : client_ctx = %p user_virt_addr = 0x%08lx" " Not Found.\n", __func__, client_ctx, *user_vaddr); else DBG("%s() : client_ctx = %p kernel_virt_addr = 0x%08lx" " Not Found.\n", __func__, client_ctx, *kernel_vaddr); mutex_unlock(&client_ctx->enrty_queue_lock); return false; } } EXPORT_SYMBOL(vidc_lookup_addr_table); u32 vidc_insert_addr_table(struct video_client_ctx *client_ctx, enum buffer_dir buffer, unsigned long user_vaddr, unsigned long *kernel_vaddr, int pmem_fd, unsigned long buffer_addr_offset, unsigned int max_num_buffers, unsigned long length) { unsigned long len, phys_addr; struct file *file = NULL; u32 *num_of_buffers = NULL; u32 i; struct buf_addr_table *buf_addr_table; struct ion_handle *buff_ion_handle = NULL; unsigned long ionflag = 0; unsigned long iova = 0; int ret = 0; unsigned long buffer_size = 0; size_t ion_len; if (!client_ctx || !length) return false; mutex_lock(&client_ctx->enrty_queue_lock); if (buffer == BUFFER_TYPE_INPUT) { buf_addr_table = client_ctx->input_buf_addr_table; num_of_buffers = &client_ctx->num_of_input_buffers; DBG("%s(): buffer = INPUT #Buf = %d\n", __func__, *num_of_buffers); } else { buf_addr_table = client_ctx->output_buf_addr_table; num_of_buffers = &client_ctx->num_of_output_buffers; DBG("%s(): buffer = OUTPUT #Buf = %d\n", __func__, *num_of_buffers); length = length * 2; /* workaround for iommu video h/w bug */ } if (*num_of_buffers == max_num_buffers) { ERR("%s(): Num of buffers reached max value : %d", __func__, max_num_buffers); goto bail_out_add; } i = 0; while (i < *num_of_buffers && user_vaddr != buf_addr_table[i].user_vaddr) i++; if (i < *num_of_buffers) { DBG("%s() : client_ctx = %p." " user_virt_addr = 0x%08lx already set", __func__, client_ctx, user_vaddr); goto bail_out_add; } else { if (!vcd_get_ion_status()) { pr_err("PMEM not available\n"); goto bail_out_add; } else { buff_ion_handle = ion_import_dma_buf( client_ctx->user_ion_client, pmem_fd); if (IS_ERR_OR_NULL(buff_ion_handle)) { ERR("%s(): get_ION_handle failed\n", __func__); goto bail_out_add; } if (ion_handle_get_flags(client_ctx->user_ion_client, buff_ion_handle, &ionflag)) { ERR("%s():ION flags fail\n", __func__); goto bail_out_add; } *kernel_vaddr = (unsigned long) ion_map_kernel( client_ctx->user_ion_client, buff_ion_handle); if (IS_ERR_OR_NULL((void *)*kernel_vaddr)) { ERR("%s():ION virtual addr fail\n", __func__); *kernel_vaddr = (unsigned long)NULL; goto ion_free_error; } if (res_trk_check_for_sec_session() || (res_trk_get_core_type() == (u32)VCD_CORE_720P)) { if (ion_phys(client_ctx->user_ion_client, buff_ion_handle, &phys_addr, &ion_len)) { ERR("%s():ION physical addr fail\n", __func__); goto ion_map_error; } len = (unsigned long) ion_len; buf_addr_table[*num_of_buffers].client_data = NULL; buf_addr_table[*num_of_buffers].dev_addr = phys_addr; } else { ret = ion_map_iommu(client_ctx->user_ion_client, buff_ion_handle, VIDEO_DOMAIN, VIDEO_MAIN_POOL, SZ_8K, length, (unsigned long *) &iova, (unsigned long *) &buffer_size, 0, ION_IOMMU_UNMAP_DELAYED); if (ret || !iova) { ERR( "%s():ION iommu map fail, ret = %d, iova = 0x%lx\n", __func__, ret, iova); goto ion_map_error; } phys_addr = iova; buf_addr_table[*num_of_buffers].client_data = NULL; buf_addr_table[*num_of_buffers].dev_addr = iova; } } phys_addr += buffer_addr_offset; (*kernel_vaddr) += buffer_addr_offset; buf_addr_table[*num_of_buffers].user_vaddr = user_vaddr; buf_addr_table[*num_of_buffers].kernel_vaddr = *kernel_vaddr; buf_addr_table[*num_of_buffers].pmem_fd = pmem_fd; buf_addr_table[*num_of_buffers].file = file; buf_addr_table[*num_of_buffers].phy_addr = phys_addr; buf_addr_table[*num_of_buffers].buff_ion_handle = buff_ion_handle; buf_addr_table[*num_of_buffers].buff_ion_flag = ionflag; *num_of_buffers = *num_of_buffers + 1; DBG("%s() : client_ctx = %p, user_virt_addr = 0x%08lx, " "kernel_vaddr = 0x%08lx phys_addr=%lu inserted!", __func__, client_ctx, user_vaddr, *kernel_vaddr, phys_addr); } mutex_unlock(&client_ctx->enrty_queue_lock); return true; ion_map_error: if (*kernel_vaddr && buff_ion_handle) ion_unmap_kernel(client_ctx->user_ion_client, buff_ion_handle); ion_free_error: if (!IS_ERR_OR_NULL(buff_ion_handle)) ion_free(client_ctx->user_ion_client, buff_ion_handle); bail_out_add: mutex_unlock(&client_ctx->enrty_queue_lock); return false; } EXPORT_SYMBOL(vidc_insert_addr_table); /* * Similar to vidc_insert_addr_table except intended for in-kernel * use where buffers have already been alloced and mapped properly */ u32 vidc_insert_addr_table_kernel(struct video_client_ctx *client_ctx, enum buffer_dir buffer, unsigned long user_vaddr, unsigned long kernel_vaddr, unsigned long phys_addr, unsigned int max_num_buffers, unsigned long length) { u32 *num_of_buffers = NULL; u32 i; struct buf_addr_table *buf_addr_table; struct msm_mapped_buffer *mapped_buffer = NULL; if (!client_ctx || !length || !kernel_vaddr || !phys_addr) return false; mutex_lock(&client_ctx->enrty_queue_lock); if (buffer == BUFFER_TYPE_INPUT) { buf_addr_table = client_ctx->input_buf_addr_table; num_of_buffers = &client_ctx->num_of_input_buffers; DBG("%s(): buffer = INPUT #Buf = %d\n", __func__, *num_of_buffers); } else { buf_addr_table = client_ctx->output_buf_addr_table; num_of_buffers = &client_ctx->num_of_output_buffers; DBG("%s(): buffer = OUTPUT #Buf = %d\n", __func__, *num_of_buffers); } if (*num_of_buffers == max_num_buffers) { ERR("%s(): Num of buffers reached max value : %d", __func__, max_num_buffers); goto bail_out_add; } i = 0; while (i < *num_of_buffers && user_vaddr != buf_addr_table[i].user_vaddr) { i++; } if (i < *num_of_buffers) { DBG("%s() : client_ctx = %p." " user_virt_addr = 0x%08lx already set", __func__, client_ctx, user_vaddr); goto bail_out_add; } else { mapped_buffer = NULL; buf_addr_table[*num_of_buffers].client_data = (void *) mapped_buffer; buf_addr_table[*num_of_buffers].dev_addr = phys_addr; buf_addr_table[*num_of_buffers].user_vaddr = user_vaddr; buf_addr_table[*num_of_buffers].kernel_vaddr = kernel_vaddr; buf_addr_table[*num_of_buffers].pmem_fd = -1; buf_addr_table[*num_of_buffers].file = NULL; buf_addr_table[*num_of_buffers].phy_addr = phys_addr; buf_addr_table[*num_of_buffers].buff_ion_handle = NULL; *num_of_buffers = *num_of_buffers + 1; DBG("%s() : client_ctx = %p, user_virt_addr = 0x%08lx, " "kernel_vaddr = 0x%08lx inserted!", __func__, client_ctx, user_vaddr, kernel_vaddr); } mutex_unlock(&client_ctx->enrty_queue_lock); return true; bail_out_add: mutex_unlock(&client_ctx->enrty_queue_lock); return false; } EXPORT_SYMBOL(vidc_insert_addr_table_kernel); u32 vidc_delete_addr_table(struct video_client_ctx *client_ctx, enum buffer_dir buffer, unsigned long user_vaddr, unsigned long *kernel_vaddr) { u32 *num_of_buffers = NULL; u32 i; struct buf_addr_table *buf_addr_table; if (!client_ctx) return false; mutex_lock(&client_ctx->enrty_queue_lock); if (buffer == BUFFER_TYPE_INPUT) { buf_addr_table = client_ctx->input_buf_addr_table; num_of_buffers = &client_ctx->num_of_input_buffers; } else { buf_addr_table = client_ctx->output_buf_addr_table; num_of_buffers = &client_ctx->num_of_output_buffers; } if (!*num_of_buffers) goto bail_out_del; i = 0; while (i < *num_of_buffers && user_vaddr != buf_addr_table[i].user_vaddr) i++; if (i == *num_of_buffers) { pr_err("%s() : client_ctx = %p." " user_virt_addr = 0x%08lx NOT found", __func__, client_ctx, user_vaddr); goto bail_out_del; } *kernel_vaddr = buf_addr_table[i].kernel_vaddr; if (buf_addr_table[i].buff_ion_handle) { ion_unmap_kernel(client_ctx->user_ion_client, buf_addr_table[i].buff_ion_handle); if (!res_trk_check_for_sec_session() && (res_trk_get_core_type() != (u32)VCD_CORE_720P)) { ion_unmap_iommu(client_ctx->user_ion_client, buf_addr_table[i].buff_ion_handle, VIDEO_DOMAIN, VIDEO_MAIN_POOL); } ion_free(client_ctx->user_ion_client, buf_addr_table[i].buff_ion_handle); buf_addr_table[i].buff_ion_handle = NULL; } if (i < (*num_of_buffers - 1)) { buf_addr_table[i].client_data = buf_addr_table[*num_of_buffers - 1].client_data; buf_addr_table[i].dev_addr = buf_addr_table[*num_of_buffers - 1].dev_addr; buf_addr_table[i].user_vaddr = buf_addr_table[*num_of_buffers - 1].user_vaddr; buf_addr_table[i].kernel_vaddr = buf_addr_table[*num_of_buffers - 1].kernel_vaddr; buf_addr_table[i].phy_addr = buf_addr_table[*num_of_buffers - 1].phy_addr; buf_addr_table[i].pmem_fd = buf_addr_table[*num_of_buffers - 1].pmem_fd; buf_addr_table[i].file = buf_addr_table[*num_of_buffers - 1].file; buf_addr_table[i].buff_ion_handle = buf_addr_table[*num_of_buffers - 1].buff_ion_handle; } *num_of_buffers = *num_of_buffers - 1; DBG("%s() : client_ctx = %p." " user_virt_addr = 0x%08lx is found and deleted", __func__, client_ctx, user_vaddr); mutex_unlock(&client_ctx->enrty_queue_lock); return true; bail_out_del: mutex_unlock(&client_ctx->enrty_queue_lock); return false; } EXPORT_SYMBOL(vidc_delete_addr_table); u32 vidc_timer_create(void (*timer_handler)(void *), void *user_data, void **timer_handle) { struct vidc_timer *hw_timer = NULL; if (!timer_handler || !timer_handle) { DBG("%s(): timer creation failed\n ", __func__); return false; } hw_timer = kzalloc(sizeof(struct vidc_timer), GFP_KERNEL); if (!hw_timer) { DBG("%s(): timer creation failed in allocation\n ", __func__); return false; } init_timer(&hw_timer->hw_timeout); hw_timer->hw_timeout.data = (unsigned long)hw_timer; hw_timer->hw_timeout.function = vidc_timer_fn; hw_timer->cb_func = timer_handler; hw_timer->userdata = user_data; *timer_handle = hw_timer; return true; } EXPORT_SYMBOL(vidc_timer_create); void vidc_timer_release(void *timer_handle) { kfree(timer_handle); } EXPORT_SYMBOL(vidc_timer_release); void vidc_timer_start(void *timer_handle, u32 time_out) { struct vidc_timer *hw_timer = (struct vidc_timer *)timer_handle; DBG("%s(): start timer\n ", __func__); if (hw_timer) { hw_timer->hw_timeout.expires = jiffies + 1*HZ; add_timer(&hw_timer->hw_timeout); } } EXPORT_SYMBOL(vidc_timer_start); void vidc_timer_stop(void *timer_handle) { struct vidc_timer *hw_timer = (struct vidc_timer *)timer_handle; DBG("%s(): stop timer\n ", __func__); if (hw_timer) del_timer(&hw_timer->hw_timeout); } EXPORT_SYMBOL(vidc_timer_stop); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Video decoder/encoder driver Init Module"); MODULE_VERSION("1.0"); module_init(vidc_init); module_exit(vidc_exit);