Files
SDK_SG200x_V2/osdrv/interdrv/v2/jpeg/cvi_jpeg.c
sophgo-forum-service 80d4596c1b osdrv: weekly rls 2024.05.22
- 3fa71d3, [feat](saradc):Add pm api.
- cfc34b, [fix](pwm):Change pwm setting don't need to disable pwm.

Change-Id: If0f2ef94f163fd534ddcd8a07444081e7bc0fb5a
2024-05-31 14:58:30 +08:00

693 lines
17 KiB
C

/*
* Copyright (C) Cvitek Co., Ltd. 2019-2021. All rights reserved.
*
* File Name: cvi_jpeg.c
* Description: jpeg system interface
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/of_reserved_mem.h>
#include <linux/streamline_annotate.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#endif
#include "cvi_jpeg.h"
#include "jpeg_common.h"
#define VERSION "CVITEK"
//#define ENABLE_DEBUG_MSG
#ifdef ENABLE_DEBUG_MSG
#define DPRINTK(args...) pr_info(args)
#else
#define DPRINTK(args...)
#endif
#define JPU_PLATFORM_DEVICE_NAME "jpeg"
#define JPU_CLK_NAME "jpeg_clk"
#define JPU_CLASS_NAME "jpu"
#define JPU_DEV_NAME "jpu"
#define JPU_REG_SIZE 0x300
#define JPU_CONTROL_REG_ADDR 0x50008008
#define JPU_CONTROL_REG_SIZE 0x4
#ifndef VM_RESERVED // for kernel up to 3.7.0 version
#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
#define MJPEG_PIC_STATUS_REG 0x4
#define MJPEG_INTR_MASK_REG 0x0C0
static DEFINE_SEMAPHORE(s_jpu_sem);
int jpu_mask = JPU_MASK_ERR;
module_param(jpu_mask, int, 0644);
static int jpu_chn_idx = -1;
extern wait_queue_head_t tWaitQueue[];
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
// global variable to avoid kernal config mismatch in filp->private_data
static void *pCviJpuDevice;
#endif
static int cvi_jpu_register_cdev(struct cvi_jpu_device *jdev);
static void cvi_jpu_unregister_cdev(struct platform_device *pdev);
#ifndef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
static int jpu_allocate_memory(struct cvi_jpu_device *jdev, struct platform_device *pdev);
#endif
static void set_clock_enable(struct cvi_jpu_device *jdev, int enable)
{
if (jdev->pdata->quirks & (JPU_QUIRK_SUPPORT_CLOCK_CONTROL | JPU_QUIRK_SUPPORT_FPGA)) {
if (enable) {
if (jdev->pdata->ops->clk_enable)
jdev->pdata->ops->clk_enable(jdev);
} else {
if (jdev->pdata->ops->clk_disable)
jdev->pdata->ops->clk_disable(jdev);
}
}
}
irqreturn_t jpu_irq_handler(int irq, void *data)
{
struct cvi_jpu_device *jdev = data;
jpudrv_buffer_t *pReg = &jdev->jpu_register;
if (jpu_chn_idx == -1)
return IRQ_HANDLED;
writel(0xFF, pReg->virt_addr + MJPEG_INTR_MASK_REG);//disable_irq_nosync(jdev->jpu_irq);
if (jdev->async_queue)
kill_fasync(&jdev->async_queue, SIGIO,
POLL_IN); // notify the interrupt to userspace
jdev->interrupt_flag = 1;
wake_up(&tWaitQueue[jpu_chn_idx]);
wake_up(&jdev->interrupt_wait_q);
return IRQ_HANDLED;
}
// JDI_IOCTL_WAIT_INTERRUPT
int jpu_wait_interrupt(int timeout)
{
u32 u32TimeOut;
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
u32TimeOut = (u32)timeout;
if (!wait_event_timeout(
jdev->interrupt_wait_q, jdev->interrupt_flag != 0,
msecs_to_jiffies(u32TimeOut))) {
return -ETIME;
}
if (signal_pending(current)) {
return -ERESTARTSYS;
}
jdev->interrupt_flag = 0;
return 0;
}
EXPORT_SYMBOL(jpu_wait_interrupt);
// JDI_IOCTL_SET_CLOCK_GATE
int jpu_set_clock_gate(int *pEnable)
{
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
u32 clkgate;
clkgate = *(u32 *)pEnable;
set_clock_enable(jdev, clkgate);
return 0;
}
EXPORT_SYMBOL(jpu_set_clock_gate);
// JDI_IOCTL_GET_INSTANCE_POOL
int jpu_get_instance_pool(jpudrv_buffer_t *p_jdb)
{
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
down(&s_jpu_sem);
if (jdev->jpu_instance_pool.base == 0) {
memcpy(&jdev->jpu_instance_pool, (struct jpudrv_buffer_t *)p_jdb,
sizeof(struct jpudrv_buffer_t));
#ifdef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
jdev->jpu_instance_pool.size =
PAGE_ALIGN(jdev->jpu_instance_pool.size);
jdev->jpu_instance_pool.phys_addr =
jdev->jpu_instance_pool.base =
(unsigned long)vmalloc(
jdev->jpu_instance_pool.size);
if (jdev->jpu_instance_pool.base == 0) {
up(&s_jpu_sem);
return -EFAULT;
}
memset((void *)(uintptr_t)jdev->jpu_instance_pool.base, 0x0,
jdev->jpu_instance_pool.size);
#else
if (jpu_alloc_dma_buffer(jdev, &jdev->jpu_instance_pool) == -1) {
up(&s_jpu_sem);
return -EFAULT;
}
memset_io((void *)jdev->jpu_instance_pool.base, 0x0,
jdev->jpu_instance_pool.size);
#endif
JPU_DBG_INFO("jdev->jpu_instance_pool base: 0x%llx, size: %d\n",
jdev->jpu_instance_pool.base,
jdev->jpu_instance_pool.size);
}
memcpy(p_jdb, &jdev->jpu_instance_pool, sizeof(struct jpudrv_buffer_t));
up(&s_jpu_sem);
return 0;
}
EXPORT_SYMBOL(jpu_get_instance_pool);
// JDI_IOCTL_OPEN_INSTANCE
int jpu_open_instance(unsigned long *pInstIdx)
{
u32 inst_idx;
struct jpudrv_instanace_list_t *jil;
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
unsigned long flags = 0;
jil = kzalloc(sizeof(*jil), GFP_KERNEL);
if (!jil)
return -ENOMEM;
inst_idx = *(u32 *)pInstIdx;
jil->inst_idx = inst_idx;
//jil->filp = filp;
spin_lock_irqsave(&jdev->jpu_lock, flags);
list_add(&jil->list, &jdev->s_jpu_inst_list_head);
jdev->open_instance_count++;
spin_unlock_irqrestore(&jdev->jpu_lock, flags);
return 0;
}
EXPORT_SYMBOL(jpu_open_instance);
// JDI_IOCTL_CLOSE_INSTANCE
int jpu_close_instance(unsigned long *pInstIdx)
{
u32 inst_idx;
struct jpudrv_instanace_list_t *jil, *n;
bool find_jil = false;
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
unsigned long flags = 0;
inst_idx = *(u32 *)pInstIdx;
spin_lock_irqsave(&jdev->jpu_lock, flags);
list_for_each_entry_safe(jil, n, &jdev->s_jpu_inst_list_head, list) {
if (jil->inst_idx == inst_idx) {
jdev->open_instance_count--;
list_del(&jil->list);
find_jil = true;
break;
}
}
spin_unlock_irqrestore(&jdev->jpu_lock, flags);
if (find_jil) {
kfree(jil);
JPU_DBG_INFO("IOCTL_CLOSE_INSTANCE, inst_idx=%d, open_count=%d\n",
(int)inst_idx, jdev->open_instance_count);
}
return 0;
}
EXPORT_SYMBOL(jpu_close_instance);
// JDI_IOCTL_GET_INSTANCE_NUM
int jpu_get_instance_num(int *pInstNum)
{
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
down(&s_jpu_sem);
*pInstNum = jdev->open_instance_count;
JPU_DBG_INFO("IOCTL_GET_INSTANCE_NUM open_count=%d\n",
jdev->open_instance_count);
up(&s_jpu_sem);
return 0;
}
EXPORT_SYMBOL(jpu_get_instance_num);
// JDI_IOCTL_GET_REGISTER_INFO
int jpu_get_register_info(jpudrv_buffer_t *p_jdb_register)
{
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
JPU_DBG_INFO("[+]JDI_IOCTL_GET_REGISTER_INFO\n");
memcpy(p_jdb_register, &jdev->jpu_register, sizeof(struct jpudrv_buffer_t));
JPU_DBG_INFO("[-]GET_REGISTER_INFO, pa = 0x%llx, va = %p, size = %d\n",
jdev->jpu_register.phys_addr, jdev->jpu_register.virt_addr,
jdev->jpu_register.size);
return 0;
}
EXPORT_SYMBOL(jpu_get_register_info);
// JDI_IOCTL_RESET
int jpu_reset(int InstIdx)
{
return 0;
}
EXPORT_SYMBOL(jpu_reset);
unsigned long jpu_get_interrupt_flag(int chnIdx)
{
unsigned long interrupt_flag = 0;
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
struct cvi_jpu_device *jdev = (struct cvi_jpu_device *)pCviJpuDevice;
#else
struct cvi_jpu_device *jdev = filp->private_data;
#endif
interrupt_flag = (jpu_chn_idx == chnIdx) ? jdev->interrupt_flag : 0;
return interrupt_flag;
}
EXPORT_SYMBOL(jpu_get_interrupt_flag);
void jpu_set_channel_num(int chnIdx)
{
jpu_chn_idx = chnIdx;
}
EXPORT_SYMBOL(jpu_set_channel_num);
static int jpu_probe(struct platform_device *pdev)
{
int err = 0;
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct cvi_jpu_device *jdev;
struct resource *res = NULL;
jdev = devm_kzalloc(&pdev->dev, sizeof(*jdev), GFP_KERNEL);
if (!jdev)
return -ENOMEM;
memset(jdev, 0, sizeof(*jdev));
#ifdef JPU_SUPPORT_GLOBAL_DEVICE_CONTEXT
pCviJpuDevice = jdev;
#endif
jdev->dev = dev;
match = of_match_device(cvi_jpu_match_table, &pdev->dev);
if (!match)
return -EINVAL;
jdev->pdata = match->data;
if (pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
unsigned long size = resource_size(res);
jdev->jpu_register.phys_addr = res->start;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
jdev->jpu_register.virt_addr =
(__u8 *)ioremap(res->start, size);
#else
jdev->jpu_register.virt_addr =
(__u8 *)ioremap_nocache(res->start, size);
#endif
jdev->jpu_register.size = size;
JPU_DBG_INFO("jpu reg pa = 0x%llx, va = %p, size = %u\n",
jdev->jpu_register.phys_addr, jdev->jpu_register.virt_addr,
jdev->jpu_register.size);
} else {
pr_err("Unable to get base register\n");
return -EINVAL;
}
if (pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
// if platform driver is implemented
if (res) {
unsigned long size = resource_size(res);
jdev->jpu_control_register.phys_addr = res->start;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
jdev->jpu_control_register.virt_addr =
(__u8 *)ioremap(res->start, size);
#else
jdev->jpu_control_register.virt_addr =
(__u8 *)ioremap_nocache(res->start, size);
#endif
jdev->jpu_control_register.size = size;
pr_info("jpu ctrl reg pa = 0x%llx, va = %p, size = %u\n",
jdev->jpu_control_register.phys_addr,
jdev->jpu_control_register.virt_addr,
jdev->jpu_control_register.size);
} else {
//TODO get res from kernel
jdev->jpu_control_register.phys_addr = JPU_CONTROL_REG_ADDR;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
jdev->jpu_control_register.virt_addr =
(__u8 *)ioremap(
jdev->jpu_control_register.phys_addr,
JPU_CONTROL_REG_SIZE);
#else
jdev->jpu_control_register.virt_addr =
(__u8 *)ioremap_nocache(
jdev->jpu_control_register.phys_addr,
JPU_CONTROL_REG_SIZE);
#endif
jdev->jpu_control_register.size = JPU_CONTROL_REG_SIZE;
JPU_DBG_INFO("jpu ctrl reg pa = 0x%llx, va = %p, size = %u\n",
jdev->jpu_control_register.phys_addr,
jdev->jpu_control_register.virt_addr,
jdev->jpu_control_register.size);
}
err = cvi_jpu_register_cdev(jdev);
if (err < 0) {
pr_err("jpu_register_cdev\n");
goto ERROR_PROBE_DEVICE;
}
if (jdev->pdata->quirks & JPU_QUIRK_SUPPORT_CLOCK_CONTROL) {
if (jdev->pdata->ops->clk_get)
jdev->pdata->ops->clk_get(jdev); // jpu_clk_get
}
if (pdev)
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
// if platform driver is implemented
if (res) {
jdev->jpu_irq = res->start;
JPU_DBG_INFO("jpu irq number irq = %d\n", jdev->jpu_irq);
} else {
JPU_DBG_ERR("jpu irq number irq = %d\n", jdev->jpu_irq);
}
#if 0
err = request_irq(jdev->jpu_irq, jpu_irq_handler, 0, "JPU_CODEC_IRQ", jdev);
if (err) {
pr_err("[JPUDRV] : fail to register interrupt handler\n");
goto ERROR_PROBE_DEVICE;
}
#endif
#ifndef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
if (jpu_allocate_memory(jdev, pdev) < 0) {
pr_err("[JPUDRV] : fail to remap jpu memory\n");
goto ERROR_PROBE_DEVICE;
}
if (jmem_init(&jdev->jmem, jdev->video_memory.phys_addr, jdev->video_memory.size) <
0) {
pr_err("[JPUDRV] : fail to init vmem system\n");
goto ERROR_PROBE_DEVICE;
}
#endif
jdev->jpu_instance_pool.base = 0;
if (jdev->pdata->quirks & JPU_QUIRK_SUPPORT_SWITCH_TO_PLL && jdev->pdata->ops->config_pll) {
jdev->pdata->ops->config_pll(jdev); //cv1835_config_pll
}
init_waitqueue_head(&jdev->interrupt_wait_q);
spin_lock_init(&jdev->jpu_lock);
INIT_LIST_HEAD(&jdev->s_jbp_head);
INIT_LIST_HEAD(&jdev->s_jpu_inst_list_head);
platform_set_drvdata(pdev, jdev);
return 0;
ERROR_PROBE_DEVICE:
if (jdev->s_jpu_major)
unregister_chrdev_region(jdev->cdev_id, 1);
if (jdev->jpu_register.virt_addr) {
iounmap((void *)jdev->jpu_register.virt_addr);
jdev->jpu_register.virt_addr = 0x00;
}
if (jdev->jpu_control_register.virt_addr) {
iounmap((void *)jdev->jpu_control_register.virt_addr);
jdev->jpu_control_register.virt_addr = 0x00;
}
return err;
}
static int cvi_jpu_register_cdev(struct cvi_jpu_device *jdev)
{
int err = 0;
jdev->jpu_class = class_create(THIS_MODULE, JPU_CLASS_NAME);
if (IS_ERR(jdev->jpu_class)) {
pr_err("create class failed\n");
return PTR_ERR(jdev->jpu_class);
}
/* get the major number of the character device */
if ((alloc_chrdev_region(&jdev->cdev_id, 0, 1, JPU_DEV_NAME)) < 0) {
err = -EBUSY;
pr_err("could not allocate major number\n");
return err;
}
jdev->s_jpu_major = MAJOR(jdev->cdev_id);
return err;
}
#ifndef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
static int jpu_allocate_memory(struct cvi_jpu_device *jdev, struct platform_device *pdev)
{
struct device_node *target = NULL;
struct reserved_mem *prmem = NULL;
if (pdev) {
target =
of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
}
if (target) {
prmem = of_reserved_mem_lookup(target);
of_node_put(target);
if (!prmem) {
pr_err("[JPUDRV]: cannot acquire memory-region\n");
return -1;
}
} else {
pr_err("[JPUDRV]: cannot find the node, memory-region\n");
return -1;
}
JPU_DBG_INFO("pool name = %s, size = 0x%llx, base = 0x%llx\n",
prmem->name, prmem->size, prmem->base);
jdev->video_memory.phys_addr = (unsigned long)prmem->base;
jdev->video_memory.size = (unsigned int)prmem->size;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
jdev->video_memory.base = (unsigned long)devm_ioremap(
&pdev->dev, jdev->video_memory.phys_addr, jdev->video_memory.size);
#else
jdev->video_memory.base = (unsigned long)devm_ioremap_nocache(
&pdev->dev, jdev->video_memory.phys_addr, jdev->video_memory.size);
#endif
if (!jdev->video_memory.base) {
pr_err("[JPUDRV] ioremap fail!\n");
pr_err("jdev->video_memory.base = 0x%llx\n", jdev->video_memory.base);
return -1;
}
JPU_DBG_INFO("reserved mem pa = 0x%llx, base = 0x%llx, size = 0x%x\n",
jdev->video_memory.phys_addr, jdev->video_memory.base,
jdev->video_memory.size);
JPU_DBG_INFO("success to probe jpu\n");
return 0;
}
#endif // #ifndef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
static int jpu_remove(struct platform_device *pdev)
{
struct cvi_jpu_device *jdev = platform_get_drvdata(pdev);
pr_info("jpu_remove\n");
if (jdev->jpu_instance_pool.base) {
#ifdef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
vfree((const void *)(uintptr_t)jdev->jpu_instance_pool.base);
#else
jpu_free_dma_buffer(jdev, &jdev->jpu_instance_pool);
#endif
jdev->jpu_instance_pool.base = 0;
}
#ifndef USE_VMALLOC_FOR_INSTANCE_POOL_MEMORY
if (jdev->video_memory.base) {
jdev->video_memory.base = 0;
jmem_exit(&jdev->jmem);
}
#endif
cvi_jpu_unregister_cdev(pdev);
if (jdev->jpu_irq)
free_irq(jdev->jpu_irq, jdev);
if (jdev->jpu_register.virt_addr) {
iounmap((void *)jdev->jpu_register.virt_addr);
jdev->jpu_register.virt_addr = 0x00;
}
if (jdev->jpu_control_register.virt_addr) {
iounmap((void *)jdev->jpu_control_register.virt_addr);
jdev->jpu_control_register.virt_addr = 0x00;
}
return 0;
}
static void cvi_jpu_unregister_cdev(struct platform_device *pdev)
{
struct cvi_jpu_device *jdev = platform_get_drvdata(pdev);
if (jdev->s_jpu_major > 0) {
device_destroy(jdev->jpu_class, jdev->cdev_id);
class_destroy(jdev->jpu_class);
cdev_del(&jdev->cdev);
unregister_chrdev_region(jdev->cdev_id, 1);
jdev->s_jpu_major = 0;
}
}
#ifdef CONFIG_PM
static int jpu_suspend(struct platform_device *pdev, pm_message_t state)
{
struct cvi_jpu_device *jdev = platform_get_drvdata(pdev);
set_clock_enable(jdev, JPEG_CLK_DISABLE);
return 0;
}
static int jpu_resume(struct platform_device *pdev)
{
struct cvi_jpu_device *jdev = platform_get_drvdata(pdev);
set_clock_enable(jdev, JPEG_CLK_ENABLE);
return 0;
}
#else
#define jpu_suspend NULL
#define jpu_resume NULL
#endif /* !CONFIG_PM */
static struct platform_driver jpu_driver = {
.driver = {
.owner = THIS_MODULE,
.name = JPU_PLATFORM_DEVICE_NAME,
.of_match_table = cvi_jpu_match_table,
},
.probe = jpu_probe,
.remove = jpu_remove,
#ifdef CONFIG_PM
.suspend = jpu_suspend,
.resume = jpu_resume,
#endif /* !CONFIG_PM */
};
static int __init jpu_init(void)
{
int res = 0;
res = platform_driver_register(&jpu_driver);
pr_info("end jpu_init result = 0x%x\n", res);
return res;
}
static void __exit jpu_exit(void)
{
pr_info("jpu_exit\n");
platform_driver_unregister(&jpu_driver);
}
MODULE_AUTHOR("Cvitek");
MODULE_DESCRIPTION("JPEG linux driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
module_init(jpu_init);
module_exit(jpu_exit);