sdk-hwV1.3/lichee/linux-4.9/drivers/media/platform/sunxi-eise/eise.c

578 lines
14 KiB
C
Raw Blame History

/*
* drivers\media\platform\ise
* Copyright (c) 2018 by Allwinnertech Co., Ltd.
* http://www.allwinnertech.com
*
* Authors:Du Jianhao <dujianhao@allwinnertech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/time.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/mpp.h>
#include "eise.h"
#include "eise_api.h"
#define EISE_MODULE_VERS "V1.0"
#define EISE_MODULE_NAME "EISE"
#define EISE_CLK_HIGH_WATER (700)
#define EISE_CLK_LOW_WATER (300)
static unsigned int *eise_regs_init;
static unsigned int *eise_ccmu_base;
static unsigned int irq_flag;
static unsigned int eise_irq_id;
static unsigned int eise_en;
static unsigned long interrupt_times;
static unsigned long err_cnt;
struct clk *eise_moduleclk;
struct clk *eise_parent_pll_clk;
//static unsigned long eise_parent_clk_rate = 432000000;
static int clk_status;
static int frame_status;
static spinlock_t eise_spin_lock;
static struct dentry *eise_debugfs_root, *debugfsP;
#if defined(CONFIG_OF)
static struct of_device_id sunxi_eise_match[] = {
{ .compatible = "allwinner,sunxi-eise",},
{}
};
MODULE_DEVICE_TABLE(of, sunxi_eise_match);
#endif
static DECLARE_WAIT_QUEUE_HEAD(wait_eise);
static irqreturn_t eise_interrupt(unsigned long data)
{
int irq_status;
writel(0x00, eise_regs_init + (EISE_INTERRUPT_EN>>2));
irq_status = readl(eise_regs_init + (EISE_INTERRUPT_STATUS>>2));
if (0x00 != (irq_status & 0x07))
irq_flag = 1;
if (0x01 != (irq_status & 0x01))
err_cnt++;
interrupt_times++;
wake_up(&wait_eise);
return IRQ_HANDLED;
}
static int eisedev_open(struct inode *node, struct file *filp)
{
return 0;
}
static int eisedev_close(struct inode *node, struct file *filp)
{
return 0;
}
static int eise_debugfs_show_main(struct seq_file *m, void *v)
{
int eise_clk;
int eise_in_size, in_width, in_height;
int eise_out_size, out_width, out_height;
int eise_ocfg;
int stride_y_out, stride_c_out;
int plane_mode;
seq_printf(m, "---------------EISE MAIN PARAM---------------\n");
/* ISE MODULE CLK */
eise_clk = clk_get_rate(eise_moduleclk);
seq_printf(m, "ISE MODULE CLK:%dMHz\n", eise_clk/1000000);
/* W_IN<49><4E>H_IN<49><4E>FMT_IN */
eise_in_size = readl(eise_regs_init + (EISE_IN_SIZE>>2));
in_width = 0x0000ffff & eise_in_size;
in_height = eise_in_size >> 16;
seq_printf(m, "W_IN:%d\n", in_width);
seq_printf(m, "H_IN:%d\n", in_height);
/* W_OUT<55><54>H_OUT<55><54>FMT_OUT */
eise_out_size = readl(eise_regs_init + (EISE_OUT_SIZE>>2));
out_width = 0x0000ffff & eise_out_size;
out_height = eise_out_size >> 16;
seq_printf(m, "W_OUT:%d\n", out_width);
seq_printf(m, "H_OUT:%d\n", out_height);
/* FLIP<49><50>MIRR */
eise_ocfg = readl(eise_regs_init + (EISE_OCFG_REG>>2));
/*
flip = 0X10 & eise_ocfg;
if (flip)
seq_printf(m, "FLIP:TRUE\n");
else
seq_printf(m, "FLIP:FALSE\n");
mirror = 0X08 & eise_ocfg;
if (mirror)
seq_printf(m, "MIRR:TRUE\n");
else
seq_printf(m, "MIRR:FALSE\n");
*/
stride_y_out = ((0X3ff00000 & eise_ocfg) >> 20) * 8;
seq_printf(m, "STRIDE_Y_OUT:%d\n", stride_y_out);
stride_c_out = ((0X3ff00 & eise_ocfg) >> 8) * 8;
seq_printf(m, "STRIDE_C_OUT:%d\n", stride_c_out);
plane_mode = 0X03 & eise_ocfg;
switch (plane_mode) {
case 0:
seq_printf(m, "FMT_OUT:NV12\n");
break;
case 1:
seq_printf(m, "FMT_OUT:NV21\n");
break;
default:
seq_printf(m, "FMT_OUT:OUT FORMAT ERR!\n");
}
return 0;
}
static int eise_debugfs_show_status(struct seq_file *m, void *v)
{
int eise_interrupt_status;
int time_out;
int eise_err_flag;
int roi_err;
seq_printf(m, "------------------ISE STATUS------------------\n");
/* INTERRUPT_TIMES */
seq_printf(m, "INTERRUPT_TIMES:%ld\n", interrupt_times);
seq_printf(m, "ISE_ERR_CNT:%ld\n", err_cnt);
/* TIMEOUT_ERR */
eise_interrupt_status = readl(eise_regs_init + (EISE_INTERRUPT_STATUS>>2));
time_out = 0x04 & eise_interrupt_status;
if (time_out)
seq_printf(m, "TIMEOUT_ERR:YES\n");
else
seq_printf(m, "TIMEOUT_ERR:NO\n");
/* ROI_ERR */
eise_err_flag = readl(eise_regs_init + (EISE_ERROR_FLAG>>2));
roi_err = 0x08 & eise_err_flag;
if (roi_err)
seq_printf(m, "ROI_ERR:YES\n");
else
seq_printf(m, "ROI_ERR:NO\n");
return 0;
}
static int eise_debugfs_show(struct seq_file *m, void *v)
{
seq_printf(m, "\n[%s] Version:[%s Debug]!\n", EISE_MODULE_NAME, EISE_MODULE_VERS);
eise_debugfs_show_main(m, v);
eise_debugfs_show_status(m, v);
return 0;
}
static int eise_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, eise_debugfs_show, NULL);
}
static long eise_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct eise_register reg;
unsigned int rData = 0x5a5a5a5a;
long ret = 0;
if (unlikely(copy_from_user(&reg, (void __user *)arg, sizeof(struct eise_register))))
return -EFAULT;
else {
switch (cmd) {
case EISE_WRITE_REGISTER:
writel(reg.value, eise_regs_init + (reg.addr>>2));
/*
eise_debug("[EISE] write to %#010x, value %#010x\n",
(unsigned int)(eise_regs_init+(reg.addr>>2)), reg.value);
rData = readl(eise_regs_init + (reg.addr>>2));
eise_debug("[EISE] read from %#010x, value %#010x\n",
(unsigned int)(eise_regs_init+(reg.addr>>2)), rData);
*/
return 0;
case EISE_READ_REGISTER:
rData = readl(eise_regs_init + (reg.addr>>2));
/* eise_debug("[EISE] read from %#010x, value %#010x\n",
(unsigned int)(eise_regs_init+(reg.addr>>2)), rData);*/
return rData;
case ENABLE_EISE:
if (clk_prepare_enable(eise_moduleclk))
eise_err("[SYS] enable ise_moduleclk failed!\n");
eise_en = 1;
rData = readl(eise_ccmu_base + (PLL_ISE_CTRL_REG>>2));
writel(0xb9002301 | rData,
eise_ccmu_base + (PLL_ISE_CTRL_REG>>2));
rData = readl(eise_ccmu_base + (EISE_CLK_REG>>2));
writel(0x80000000 | rData,
eise_ccmu_base + (EISE_CLK_REG>>2));
rData = readl(eise_ccmu_base + (MBUS_CLK_GATING_REG>>2));
writel(0x00802001 | rData,
eise_ccmu_base + (MBUS_CLK_GATING_REG>>2));
rData = readl(eise_ccmu_base + (EISE_BGR_REG>>2));
writel(0x00010001 | rData,
eise_ccmu_base + (EISE_BGR_REG>>2));
/*
rData = readl(eise_ccmu_base+ (PLL_ISE_CTRL_REG>>2));
eise_debug("[EISE] read from %#010x, value %#010x\n",
(unsigned int)(eise_ccmu_base+ (PLL_ISE_CTRL_REG>>2)), rData);
rData = readl(eise_ccmu_base+ (EISE_CLK_REG>>2));
eise_debug("[EISE] read from %#010x, value %#010x\n",
(unsigned int)(eise_ccmu_base+ (EISE_CLK_REG>>2)), rData);
rData = readl(eise_ccmu_base+ (MBUS_CLK_GATING_REG>>2));
eise_debug("[ISE] read from %#010x, value %#010x\n",
(unsigned int)(eise_ccmu_base+ (MBUS_CLK_GATING_REG>>2)), rData);
rData = readl(eise_ccmu_base+ (EISE_BGR_REG>>2));
eise_debug("[ISE] read from %#010x, value %#010x\n",
(unsigned int)(eise_ccmu_base+ (EISE_BGR_REG>>2)), rData);
*/
break;
case DISABLE_EISE:
if ((NULL == eise_moduleclk) || IS_ERR(eise_moduleclk)) {
eise_err("[SYS] ise_moduleclk is invalid!\n");
return -EFAULT;
} else {
clk_disable_unprepare(eise_moduleclk);
eise_en = 0;
//eise_print("[SYS] disable ise_moduleclk!\n");
}
break;
case WAIT_EISE_FINISH:
wait_event_timeout(wait_eise, irq_flag,
reg.value*HZ/30*3);
if (0 == irq_flag)
eise_print("[SYS] wait_event_timeout!\n");
irq_flag = 0;
frame_status = 1;
break;
case SET_EISE_FREQ:
{
unsigned int arg_rate = reg.value;
if (arg_rate >= EISE_CLK_LOW_WATER &&
arg_rate <= EISE_CLK_HIGH_WATER) {
#if defined(CONFIG_ARCH_SUN8IW19P1)
if (clk_set_rate(eise_moduleclk, arg_rate*1000000))
eise_err("set clock failed\n");
#else
unsigned long eise_parent_clk_rate = 432000000;
if (!clk_set_rate(eise_parent_pll_clk, arg_rate*1000000)) {
eise_parent_clk_rate = clk_get_rate(eise_parent_pll_clk);
if (clk_set_rate(eise_moduleclk, eise_parent_clk_rate))
eise_err("[SYS] set eise clock failed!\n");
} else {
eise_err("set pll clock failed!\n");
}
#endif
}
ret = clk_get_rate(eise_moduleclk);
eise_print("eise real_fre=%ld\n", ret);
break;
}
default:
eise_warn("[SYS] do not supprot this ioctrl now!\n");
break;
}
return 0;
}
}
int enable_eise_hw_clk(void)
{
unsigned long flags;
int res = -EFAULT;
spin_lock_irqsave(&eise_spin_lock, flags);
if (clk_status == 0) {
res = 0;
goto out;
}
clk_status = 0;
if ((NULL == eise_moduleclk) || (IS_ERR(eise_moduleclk)))
eise_err("eise_moduleclk is invalid!\n");
else
clk_prepare_enable(eise_moduleclk);
out:
spin_unlock_irqrestore(&eise_spin_lock, flags);
return 0;
}
int disable_eise_hw_clk(void)
{
unsigned long flags;
int res = -EFAULT;
spin_lock_irqsave(&eise_spin_lock, flags);
if (clk_status == 1) {
res = 1;
goto out;
}
clk_status = 1;
if ((NULL == eise_moduleclk) || (IS_ERR(eise_moduleclk)))
eise_err("eise_moduleclk is invalid!\n");
else
clk_disable_unprepare(eise_moduleclk);
out:
spin_unlock_irqrestore(&eise_spin_lock, flags);
return 0;
}
static int snd_sw_eise_suspend(struct platform_device *pdev, pm_message_t state)
{
int ret = 0;
if (0 == eise_en) {
eise_print("[SYS] wait_event_timeout!\n");
frame_status = 0;
} else {
ret = disable_eise_hw_clk();
if (ret < 0) {
eise_err("eise clk disable somewhere error!\n");
return -EFAULT;
}
}
printk("[eise]suspend: disable_eise_hw_clk finish!\n");
return 0;
}
static int snd_sw_eise_resume(struct platform_device *pdev)
{
int ret = 0;
if (eise_en == 1) {
ret = enable_eise_hw_clk();
if (ret < 0) {
eise_err("[eise] ise clk enable somewhere error!\n");
return -EFAULT;
}
printk("[eise]resume enable_ise_hw_clk finish!\n");
}
printk("[eise] standby resume finish!\n");
return 0;
}
static struct file_operations eise_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = eise_ioctl,
.open = eisedev_open,
.release = eisedev_close
};
static const struct file_operations eise_debugfs_fops = {
.owner = THIS_MODULE,
.open = eise_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct miscdevice eise_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &eise_fops
};
#if defined(CONFIG_DEBUG_FS)
int sunxi_eise_debug_register_driver(void)
{
#if defined (CONFIG_SUNXI_MPP)
eise_debugfs_root = debugfs_mpp_root;
#else
eise_debugfs_root = debugfs_create_dir("mpp", NULL);
#endif
if (eise_debugfs_root == NULL)
return -ENOENT;
debugfsP = debugfs_create_file("sunxi-eise", 0444, eise_debugfs_root,
NULL, &eise_debugfs_fops);
if (IS_ERR_OR_NULL(debugfsP)) {
printk(KERN_INFO "Warning: fail to create debugfs node!\n");
return -ENOMEM;
}
return 0;
}
#else
int sunxi_eise_debug_register_driver(void)
{
return 0;
}
#endif
void sunxi_eise_debug_unregister_driver(void)
{
debugfs_remove(debugfsP);
}
static int sunxi_eise_init(struct platform_device *pdev)
{
struct device_node *node;
int ret;
//unsigned int val;
ret = misc_register(&eise_dev);
if (ret >= 0)
eise_print("EISE device register success! %d\n", ret);
else {
eise_err("EISE device register failed! %d\n", ret);
return -EINVAL;
}
node = of_find_compatible_node(NULL, NULL, "allwinner,sunxi-eise");
eise_irq_id = irq_of_parse_and_map(node, 0);
eise_print("irq_id = %d\n", eise_irq_id);
ret = request_irq(eise_irq_id, (irq_handler_t)eise_interrupt, 0, "sunxi_eise", NULL);
if (ret < 0) {
eise_err("Request EISE Irq error! return %d\n", ret);
return -EINVAL;
} else
eise_print("Request EISE Irq success! irq_id = %d, return %d\n", eise_irq_id, ret);
eise_regs_init = (unsigned int *)of_iomap(node, 0);
eise_ccmu_base = (unsigned int *)of_iomap(node, 1);
eise_parent_pll_clk = of_clk_get(node, 0);
if ((!eise_parent_pll_clk) || IS_ERR(eise_parent_pll_clk)) {
eise_warn("[ise] try to get eise_parent_pll_clk failed!\n");
return -EINVAL;
}
eise_moduleclk = of_clk_get(node, 1);
if (!eise_moduleclk || IS_ERR(eise_moduleclk)) {
eise_warn("[eise] get eise_moduleclk failed!\n");
return -EINVAL;
}
#if defined(CONFIG_ARCH_SUN8IW19P1)
if (clk_set_parent(eise_moduleclk, eise_parent_pll_clk))
eise_err("set eise clk and parent clk failed;\n");
#endif
ret = sunxi_eise_debug_register_driver();
if (ret) {
eise_err("Sunxi eise debug register driver failed!\n");
return -EINVAL;
}
return 0;
}
static void sunxi_eise_exit(void)
{
sunxi_eise_debug_unregister_driver();
if (NULL == eise_moduleclk || IS_ERR(eise_moduleclk)) {
eise_warn("[eise] eise_moduleclk handle "
"is invalid,just return!\n");
} else {
clk_put(eise_moduleclk);
eise_moduleclk = NULL;
}
if (NULL == eise_parent_pll_clk || IS_ERR(eise_parent_pll_clk)) {
eise_warn("[eise] eise_parent_pll_clk "
"handle is invalid,just return!\n");
} else {
clk_put(eise_parent_pll_clk);
eise_parent_pll_clk = NULL;
}
eise_en = 0;
free_irq(eise_irq_id, NULL);
iounmap(eise_regs_init);
misc_deregister(&eise_dev);
eise_print("EISE device has been removed!\n");
}
static int sunxi_eise_remove(struct platform_device *pdev)
{
sunxi_eise_exit();
return 0;
}
static int sunxi_eise_probe(struct platform_device *pdev)
{
sunxi_eise_init(pdev);
return 0;
}
static struct platform_driver sunxi_eise_driver = {
.probe = sunxi_eise_probe,
.remove = sunxi_eise_remove,
.suspend = snd_sw_eise_suspend,
.resume = snd_sw_eise_resume,
.driver = {
.name = "sunxi-eise",
.owner = THIS_MODULE,
.of_match_table = sunxi_eise_match,
},
};
static int __init eise_init(void)
{
printk("sunxi eise version 1.0 \n");
return platform_driver_register(&sunxi_eise_driver);
}
static void __exit eise_exit(void)
{
platform_driver_unregister(&sunxi_eise_driver);
};
module_init(eise_init);
module_exit(eise_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("DuJianhao<dujianhao@allwinnertech.com>");
MODULE_DESCRIPTION("EISE device driver");
MODULE_VERSION("1.0");
MODULE_ALIAS("platform:EISE(AW1816)");