blob: 28113717eb23df51ad623e1df6064871cbd5bb64 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* drivers/amlogic/media/frc/frc_drv.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
/* Standard Linux headers */
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/compat.h>
#include <linux/of_irq.h>
#include <linux/of_clk.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#include <linux/ctype.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/clk.h>
#include <linux/sched/clock.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/frc/frc_reg.h>
#include <linux/amlogic/media/frc/frc_common.h>
#include <linux/amlogic/power_domain.h>
#include <dt-bindings/power/t3-pd.h>
#include <linux/amlogic/media/video_sink/video_signal_notify.h>
#include "frc_drv.h"
#include "frc_proc.h"
#include "frc_dbg.h"
#include "frc_buf.h"
#include "frc_hw.h"
#ifdef CONFIG_AMLOGIC_MEDIA_FRC_RDMA
#include "frc_rdma.h"
#endif
// static struct frc_dev_s *frc_dev; // for SWPL-53056:KASAN: use-after-free
static struct frc_dev_s frc_dev;
int frc_dbg_en;
EXPORT_SYMBOL(frc_dbg_en);
module_param(frc_dbg_en, int, 0664);
MODULE_PARM_DESC(frc_dbg_en, "frc debug level");
struct frc_dev_s *get_frc_devp(void)
{
// return frc_dev;
return &frc_dev;
}
int frc_kerdrv_ver = FRC_KERDRV_VER;
EXPORT_SYMBOL(frc_kerdrv_ver);
// static struct frc_fw_data_s *fw_data;
struct frc_fw_data_s fw_data; // important 2021_0510
static const char frc_alg_defver[] = "alg_ver:default";
struct frc_fw_data_s *frc_get_fw_data(void)
{
return &fw_data;
}
EXPORT_SYMBOL(frc_get_fw_data);
u32 sizeof_frc_fw_data_struct(void)
{
return sizeof(struct frc_fw_data_s);
}
EXPORT_SYMBOL(sizeof_frc_fw_data_struct);
ssize_t frc_reg_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
pr_frc(0, "read: echo r reg > /sys/class/frc/reg\n");
pr_frc(0, "write: echo w reg value > /sys/class/frc/reg\n");
pr_frc(0, "dump: echo d reg length > /sys/class/frc/reg\n");
return 0;
}
ssize_t frc_reg_store(struct class *class,
struct class_attribute *attr,
const char *buf,
size_t count)
{
frc_reg_io(buf);
return count;
}
ssize_t frc_tool_debug_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
struct tool_debug_s *read_parm = NULL;
struct frc_dev_s *devp = get_frc_devp();
read_parm = &devp->tool_dbg;
return sprintf(buf, "[0x%x] = 0x%x\n",
read_parm->reg_read, read_parm->reg_read_val);
}
ssize_t frc_tool_debug_store(struct class *class,
struct class_attribute *attr,
const char *buf,
size_t count)
{
struct frc_dev_s *devp = get_frc_devp();
frc_tool_dbg_store(devp, buf);
return count;
}
ssize_t frc_debug_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
struct frc_dev_s *devp = get_frc_devp();
return frc_debug_if_help(devp, buf);
}
ssize_t frc_debug_store(struct class *class,
struct class_attribute *attr,
const char *buf,
size_t count)
{
struct frc_dev_s *devp = get_frc_devp();
frc_debug_if(devp, buf, count);
return count;
}
static struct class_attribute frc_class_attrs[] = {
__ATTR(debug, 0644, frc_debug_show, frc_debug_store),
__ATTR(reg, 0644, frc_reg_show, frc_reg_store),
__ATTR(tool_debug, 0644, frc_tool_debug_show, frc_tool_debug_store),
__ATTR(final_line_param, 0644, frc_bbd_final_line_param_show,
frc_bbd_final_line_param_store),
__ATTR(vp_ctrl_param, 0644, frc_vp_ctrl_param_show, frc_vp_ctrl_param_store),
__ATTR(logo_ctrl_param, 0644, frc_logo_ctrl_param_show, frc_logo_ctrl_param_store),
__ATTR(iplogo_ctrl_param, 0644, frc_iplogo_ctrl_param_show, frc_iplogo_ctrl_param_store),
__ATTR(melogo_ctrl_param, 0644, frc_melogo_ctrl_param_show, frc_melogo_ctrl_param_store),
__ATTR(sence_chg_detect_param, 0644, frc_sence_chg_detect_param_show,
frc_sence_chg_detect_param_store),
__ATTR(fb_ctrl_param, 0644, frc_fb_ctrl_param_show, frc_fb_ctrl_param_store),
__ATTR(me_ctrl_param, 0644, frc_me_ctrl_param_show, frc_me_ctrl_param_store),
__ATTR(search_rang_param, 0644, frc_search_rang_param_show, frc_search_rang_param_store),
__ATTR(pixel_lpf_param, 0644, frc_pixel_lpf_param_show, frc_pixel_lpf_param_store),
__ATTR(me_rule_param, 0644, frc_me_rule_param_show, frc_me_rule_param_store),
__ATTR(film_ctrl_param, 0644, frc_film_ctrl_param_show, frc_film_ctrl_param_store),
__ATTR_NULL
};
static int frc_open(struct inode *inode, struct file *file)
{
struct frc_dev_s *frc_devp;
frc_devp = container_of(inode->i_cdev, struct frc_dev_s, cdev);
file->private_data = frc_devp;
return 0;
}
static int frc_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static long frc_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct frc_dev_s *devp;
void __user *argp = (void __user *)arg;
u32 data;
u8 tmpver[32];
devp = file->private_data;
if (!devp)
return -EFAULT;
if (!devp->probe_ok)
return -EFAULT;
switch (cmd) {
case FRC_IOC_GET_FRC_EN:
data = devp->frc_en;
if (copy_to_user(argp, &data, sizeof(u32)))
ret = -EFAULT;
break;
case FRC_IOC_GET_FRC_STS:
data = (u32)devp->frc_sts.state;
if (copy_to_user(argp, &data, sizeof(u32)))
ret = -EFAULT;
break;
case FRC_IOC_SET_FRC_CANDENCE:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
pr_frc(1, "SET_FRC_CANDENCE:%d\n", data);
break;
case FRC_IOC_GET_VIDEO_LATENCY:
data = (u32)frc_get_video_latency();
if (copy_to_user(argp, &data, sizeof(u32)))
ret = -EFAULT;
break;
case FRC_IOC_GET_IS_ON:
data = (u32)frc_is_on();
if (copy_to_user(argp, &data, sizeof(u32)))
ret = -EFAULT;
break;
case FRC_IOC_SET_INPUT_VS_RATE:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
pr_frc(1, "SET_INPUT_VS_RATE:%d\n", data);
break;
case FRC_IOC_SET_MEMC_ON_OFF:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
pr_frc(1, "set memc_autoctrl:%d\n", data);
if (data) {
if (!devp->frc_sts.auto_ctrl) {
devp->frc_sts.auto_ctrl = true;
devp->frc_sts.re_config = true;
}
} else {
devp->frc_sts.auto_ctrl = false;
if (devp->frc_sts.state == FRC_STATE_ENABLE)
frc_change_to_state(FRC_STATE_DISABLE);
}
break;
case FRC_IOC_SET_MEMC_LEVEL:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
frc_memc_set_level(data);
// pr_frc(1, "SET_MEMC_LEVEL:%d\n", data);
break;
case FRC_IOC_SET_MEMC_DMEO_MODE:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
frc_memc_set_demo(data);
// pr_frc(1, "SET_MEMC_DEMO:%d\n", data);
break;
case FRC_IOC_GET_MEMC_VERSION:
strncpy(&tmpver[0], &fw_data.frc_alg_ver[0], sizeof(u8) * 32);
if (copy_to_user(argp, tmpver, sizeof(u8) * 32))
ret = -EFAULT;
break;
}
return ret;
}
static const struct dts_match_data dts_match_t3 = {
.chip = ID_T3,
};
static const struct of_device_id frc_dts_match[] = {
{
.compatible = "amlogic, t3_frc",
.data = &dts_match_t3,
},
};
static int frc_attach_pd(struct frc_dev_s *devp)
{
struct platform_device *pdev = devp->pdev;
struct device_link *link;
char *pd_name[3] = {"frc-top", "frc-me", "frc-mc"};
int i;
u32 pd_cnt = 3;
if (pdev->dev.pm_domain) {
pr_frc(0, "%s err pm domain\n", __func__);
return -1;
}
for (i = 0; i < pd_cnt; i++) {
devp->pd_dev = dev_pm_domain_attach_by_name(&pdev->dev, pd_name[i]);
if (IS_ERR(devp->pd_dev))
return PTR_ERR(devp->pd_dev);
if (!devp->pd_dev)
return -1;
link = device_link_add(&pdev->dev, devp->pd_dev,
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
if (!link) {
pr_frc(0, "%s fail to add device_link idx (%d) pd\n", __func__, i);
return -EINVAL;
}
//pr_frc(1, "pw domain %s attach\n", pd_name[i]);
}
return 0;
}
void frc_power_domain_ctrl(struct frc_dev_s *devp, u32 onoff)
{
struct frc_data_s *frc_data;
struct frc_fw_data_s *pfw_data;
enum chip_id chip;
frc_data = (struct frc_data_s *)devp->data;
pfw_data = (struct frc_fw_data_s *)devp->fw_data;
chip = frc_data->match_data->chip;
#define K_MEMC_CLK_DIS
if (devp->power_on_flag == onoff) {
pr_frc(0, "warning: same pw state\n");
return;
}
if (!onoff) {
// devp->power_on_flag = false;
frc_change_to_state(FRC_STATE_BYPASS);
set_frc_enable(false);
set_frc_bypass(true);
frc_state_change_finish(devp);
devp->power_on_flag = false;
// devp->power_off_flag = 0;
}
if (chip == ID_T3) {
if (onoff) {
#ifdef K_MEMC_CLK_DIS
pwr_ctrl_psci_smc(PDID_T3_FRCTOP, PWR_ON);
pwr_ctrl_psci_smc(PDID_T3_FRCME, PWR_ON);
pwr_ctrl_psci_smc(PDID_T3_FRCMC, PWR_ON);
#endif
devp->power_on_flag = true;
frc_init_config(devp);
frc_buf_config(devp);
frc_internal_initial(devp);
frc_hw_initial(devp);
if (pfw_data->frc_fw_reinit)
pfw_data->frc_fw_reinit();
} else {
#ifdef K_MEMC_CLK_DIS
pwr_ctrl_psci_smc(PDID_T3_FRCTOP, PWR_OFF);
pwr_ctrl_psci_smc(PDID_T3_FRCME, PWR_OFF);
pwr_ctrl_psci_smc(PDID_T3_FRCMC, PWR_OFF);
#endif
}
pr_frc(0, "t3 power domain power %d\n", onoff);
}
// if (onoff)
// devp->power_on_flag = true;
}
static int frc_dts_parse(struct frc_dev_s *frc_devp)
{
struct device_node *of_node;
unsigned int val;
int ret = 0;
const struct of_device_id *of_id;
struct platform_device *pdev = frc_devp->pdev;
struct resource *res;
resource_size_t *base;
struct frc_data_s *frc_data;
of_node = pdev->dev.of_node;
of_id = of_match_device(frc_dts_match, &pdev->dev);
if (of_id) {
PR_FRC("%s\n", of_id->compatible);
frc_data = frc_devp->data;
frc_data->match_data = of_id->data;
PR_FRC("chip id:%d\n", frc_data->match_data->chip);
}
if (of_node) {
ret = of_property_read_u32(of_node, "frc_en", &val);
if (ret) {
PR_FRC("Can't find frc_en.\n");
frc_devp->frc_en = 0;
} else {
frc_devp->frc_en = val;
}
ret = of_property_read_u32(of_node, "frc_hw_pos", &val);
if (ret)
PR_FRC("Can't find frc_hw_pos.\n");
else
frc_devp->frc_hw_pos = val;
}
pr_frc(0, "frc_en:%d, frc_hw_pos:%d\n", frc_devp->frc_en, frc_devp->frc_hw_pos);
/*get irq number from dts*/
frc_devp->in_irq = of_irq_get_byname(of_node, "irq_frc_in");
snprintf(frc_devp->in_irq_name, sizeof(frc_devp->in_irq_name), "frc_input_irq");
PR_FRC("%s=%d\n", frc_devp->in_irq_name, frc_devp->in_irq);
if (frc_devp->in_irq > 0) {
ret = request_irq(frc_devp->in_irq, frc_input_isr, IRQF_SHARED,
frc_devp->in_irq_name, (void *)frc_devp);
if (ret)
PR_ERR("request in irq fail\n");
else
disable_irq(frc_devp->in_irq);
}
frc_devp->out_irq = of_irq_get_byname(of_node, "irq_frc_out");
snprintf(frc_devp->out_irq_name, sizeof(frc_devp->out_irq_name), "frc_out_irq");
PR_FRC("%s=%d\n", frc_devp->out_irq_name, frc_devp->out_irq);
if (frc_devp->out_irq > 0) {
ret = request_irq(frc_devp->out_irq, frc_output_isr, IRQF_SHARED,
frc_devp->out_irq_name, (void *)frc_devp);
if (ret)
PR_ERR("request out irq fail\n");
else
disable_irq(frc_devp->out_irq);
}
frc_devp->rdma_irq = of_irq_get_byname(of_node, "irq_frc_rdma");
snprintf(frc_devp->rdma_irq_name, sizeof(frc_devp->rdma_irq_name), "frc_rdma_irq");
PR_FRC("%s=%d\n", frc_devp->rdma_irq_name, frc_devp->rdma_irq);
#ifdef CONFIG_AMLOGIC_MEDIA_FRC_RDMA
if (frc_devp->rdma_irq > 0) {
ret = request_irq(frc_devp->rdma_irq, frc_rdma_isr, IRQF_SHARED,
frc_devp->rdma_irq_name, (void *)frc_devp);
if (ret)
PR_ERR("request rdma irq fail\n");
else
disable_irq(frc_devp->rdma_irq);
}
#endif
/*register map*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "frc_reg");
if (res) {
base = devm_ioremap(&pdev->dev, res->start, res->end - res->start);
if (!base) {
PR_ERR("Unable to map reg base\n");
frc_devp->reg = NULL;
frc_base = NULL;
} else {
frc_devp->reg = (void *)base;
frc_base = frc_devp->reg;
pr_frc(0, "frc reg base 0x%lx -> 0x%lx, map size:0x%lx\n",
(ulong)res->start, (ulong)frc_base, (ulong)(res->end - res->start));
}
} else {
frc_devp->reg = NULL;
frc_base = NULL;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "frc_clk_reg");
if (res) {
base = devm_ioremap(&pdev->dev, res->start, res->end - res->start);
if (!base) {
PR_ERR("Unable to map frc clk reg base\n");
frc_devp->clk_reg = NULL;
frc_clk_base = NULL;
} else {
frc_devp->clk_reg = (void *)base;
frc_clk_base = frc_devp->clk_reg;
pr_frc(0, "clk reg base 0x%lx -> 0x%lx, map size:0x%lx\n",
(ulong)res->start, (ulong)frc_clk_base,
(ulong)(res->end - res->start));
}
} else {
frc_devp->clk_reg = NULL;
frc_clk_base = NULL;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu_reg");
if (res) {
base = devm_ioremap(&pdev->dev, res->start, res->end - res->start);
if (!base) {
PR_ERR("Unable to map vpu reg base\n");
frc_devp->vpu_reg = NULL;
vpu_base = NULL;
} else {
frc_devp->vpu_reg = (void *)base;
vpu_base = frc_devp->vpu_reg;
pr_frc(0, "vpu reg base 0x%lx -> 0x%lx, map size:0x%lx\n",
(ulong)res->start, (ulong)vpu_base, (ulong)(res->end - res->start));
}
} else {
frc_devp->vpu_reg = NULL;
vpu_base = NULL;
}
ret = of_reserved_mem_device_init(&pdev->dev);
if (ret) {
pr_frc(0, "resource undefined !\n");
frc_devp->buf.cma_mem_size = 0;
}
frc_devp->buf.cma_mem_size = dma_get_cma_size_int_byte(&pdev->dev);
pr_frc(0, "cma_mem_size=0x%x\n", frc_devp->buf.cma_mem_size);
frc_devp->clk_frc = clk_get(&pdev->dev, "clk_frc");
frc_devp->clk_me = clk_get(&pdev->dev, "clk_me");
if (IS_ERR(frc_devp->clk_me) || IS_ERR(frc_devp->clk_frc)) {
pr_frc(0, "can't get frc clk !\n");
frc_devp->clk_frc = NULL;
frc_devp->clk_me = NULL;
}
frc_attach_pd(frc_devp);
return ret;
}
#ifdef CONFIG_COMPAT
static long frc_campat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
arg = (unsigned long)compat_ptr(arg);
return frc_ioctl(file, cmd, arg);
}
#endif
int frc_notify_callback(struct notifier_block *block, unsigned long cmd, void *para)
{
struct frc_dev_s *devp = get_frc_devp();
if (!devp)
return -1;
pr_frc(1, "%s cmd: 0x%lx\n", __func__, cmd);
switch (cmd) {
case VOUT_EVENT_MODE_CHANGE_PRE:
/*if frc on, need disable frc, and enable frc*/
devp->frc_sts.out_put_mode_changed = FRC_EVENT_VOUT_CHG;
//frc_change_to_state(FRC_STATE_DISABLE);
break;
case VOUT_EVENT_MODE_CHANGE:
devp->frc_sts.out_put_mode_changed = FRC_EVENT_VOUT_CHG;
//frc_change_to_state(FRC_STATE_DISABLE);
break;
default:
break;
}
return 0;
}
int frc_vd_notify_callback(struct notifier_block *block, unsigned long cmd, void *para)
{
struct frc_dev_s *devp = get_frc_devp();
struct vd_info_s *info;
u32 flags;
if (!devp)
return -1;
info = (struct vd_info_s *)para;
flags = info->flags;
pr_frc(3, "%s cmd: 0x%lx flags:0x%x\n", __func__, cmd, flags);
switch (cmd) {
case VIDEO_INFO_CHANGED:
/*if frc on, need disable frc, and enable frc*/
if (((flags & VIDEO_SIZE_CHANGE_EVENT)
== VIDEO_SIZE_CHANGE_EVENT) && devp->probe_ok) {
set_frc_enable(false);
set_frc_bypass(true);
frc_change_to_state(FRC_STATE_DISABLE);
frc_state_change_finish(devp);
pr_frc(1, "VIDEO_SIZE_CHANGE_EVENT\n");
devp->frc_sts.out_put_mode_changed = FRC_EVENT_VF_CHG_IN_SIZE;
}
break;
default:
break;
}
return 0;
}
static struct notifier_block frc_notifier_nb = {
.notifier_call = frc_notify_callback,
};
static struct notifier_block frc_notifier_vb = {
.notifier_call = frc_vd_notify_callback,
};
static const struct file_operations frc_fops = {
.owner = THIS_MODULE,
.open = frc_open,
.release = frc_release,
.unlocked_ioctl = frc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = frc_campat_ioctl,
#endif
};
static void frc_clock_workaround(struct work_struct *work)
{
struct frc_dev_s *devp = container_of(work,
struct frc_dev_s, frc_clk_work);
if (unlikely(!devp)) {
PR_ERR("%s err, devp is NULL\n", __func__);
return;
}
if (!devp->probe_ok)
return;
if (!devp->power_on_flag)
return;
// pr_frc(1, "%s, clk_state:%d\n", __func__, devp->clk_state);
if (devp->clk_state == FRC_CLOCK_2MIN) {
clk_set_rate(devp->clk_frc, 333333333);
devp->clk_state = FRC_CLOCK_MIN;
} else if (devp->clk_state == FRC_CLOCK_2NOR) {
clk_set_rate(devp->clk_frc, 667000000);
devp->clk_state = FRC_CLOCK_NOR;
}
pr_frc(1, "%s, clk_new state:%d\n", __func__, devp->clk_state);
}
static void frc_drv_initial(struct frc_dev_s *devp)
{
struct vinfo_s *vinfo = get_current_vinfo();
struct frc_fw_data_s *fw_data;
u32 i;
pr_frc(0, "%s\n", __func__);
if (!devp)
return;
devp->frc_sts.state = FRC_STATE_BYPASS;
devp->frc_sts.new_state = FRC_STATE_BYPASS;
/*0:before postblend; 1:after postblend*/
//devp->frc_hw_pos = FRC_POS_AFTER_POSTBLEND;/*for test*/
devp->frc_sts.auto_ctrl = 0;
devp->frc_fw_pause = false;
// devp->frc_fw_pause = true;
devp->frc_sts.frame_cnt = 0;
devp->frc_sts.state_transing = false;
devp->frc_sts.re_cfg_cnt = 0;
devp->frc_sts.out_put_mode_changed = false;
devp->frc_sts.re_config = false;
devp->in_sts.vf_sts = 0;/*initial to no*/
devp->dbg_force_en = 0;
/*input sts initial*/
devp->in_sts.have_vf_cnt = 0;
devp->in_sts.no_vf_cnt = 0;
devp->dbg_in_out_ratio = FRC_RATIO_1_1;/*enum frc_ratio_mode_type frc_ratio_mode*/
// devp->dbg_in_out_ratio = FRC_RATIO_2_5;/*enum frc_ratio_mode_type frc_ratio_mode*/
devp->dbg_input_hsize = vinfo->width;
devp->dbg_input_vsize = vinfo->height;
devp->dbg_reg_monitor_i = 0;
devp->dbg_reg_monitor_o = 0;
devp->dbg_vf_monitor = 0;
for (i = 0; i < MONITOR_REG_MAX; i++) {
devp->dbg_in_reg[i] = 0;
devp->dbg_out_reg[i] = 0;
}
devp->dbg_buf_len = 0;
devp->loss_ratio = 0;
devp->prot_mode = false;
devp->in_out_ratio = FRC_RATIO_1_1;
// devp->in_out_ratio = FRC_RATIO_2_5;
// devp->film_mode = EN_FILM32;
devp->film_mode = EN_VIDEO;
devp->film_mode_det = 0;
fw_data = (struct frc_fw_data_s *)devp->fw_data;
fw_data->holdline_parm.me_hold_line = 4;
fw_data->holdline_parm.mc_hold_line = 4;
fw_data->holdline_parm.inp_hold_line = 4;
fw_data->holdline_parm.reg_post_dly_vofst = 0;/*fixed*/
fw_data->holdline_parm.reg_mc_dly_vofst0 = 1;/*fixed*/
memset(&devp->frc_crc_data, 0, sizeof(struct frc_crc_data_s));
memset(&devp->ud_dbg, 0, sizeof(struct frc_ud_s));
/*used for force in/out size for frc process*/
memset(&devp->force_size, 0, sizeof(struct frc_force_size_s));
}
void get_vout_info(struct frc_dev_s *frc_devp)
{
struct vinfo_s *vinfo = get_current_vinfo();
u32 tmpframterate = 0;
frc_devp->out_sts.vout_height = vinfo->height;
frc_devp->out_sts.vout_width = vinfo->width;
tmpframterate =
(vinfo->sync_duration_num * 100 / vinfo->sync_duration_den) / 100;
if (frc_devp->out_sts.out_framerate != tmpframterate) {
frc_devp->out_sts.out_framerate = tmpframterate;
pr_frc(0, "vout:w-%d,h-%d,rate-%d\n",
frc_devp->out_sts.vout_width,
frc_devp->out_sts.vout_height,
frc_devp->out_sts.out_framerate);
}
}
static int frc_probe(struct platform_device *pdev)
{
int ret = 0, i;
struct frc_data_s *frc_data;
struct frc_dev_s *frc_devp = &frc_dev;
// frc_dev = kzalloc(sizeof(*frc_dev), GFP_KERNEL);
// if (!frc_dev) {
// PR_ERR("%s: frc_dev kzalloc memory failed\n", __func__);
// goto fail_alloc_dev;
// }
memset(frc_devp, 0, (sizeof(struct frc_dev_s)));
frc_devp->data = NULL;
frc_devp->data = kzalloc(sizeof(*frc_devp->data), GFP_KERNEL);
if (!frc_devp->data) {
PR_ERR("%s: frc_dev->data fail\n", __func__);
goto fail_alloc_data_fail;
}
// frc_devp->fw_data = NULL;
// frc_devp->fw_data = kzalloc(sizeof(struct frc_fw_data_s), GFP_KERNEL);
frc_devp->fw_data = &fw_data;
memset(frc_devp->fw_data, 0, (sizeof(struct frc_fw_data_s)));
strncpy(&fw_data.frc_alg_ver[0], &frc_alg_defver[0],
strlen(frc_alg_defver));
if (!frc_devp->fw_data) {
PR_ERR("%s: frc_dev->fw_data fail\n", __func__);
goto fail_alloc_fw_data_fail;
}
PR_FRC("%s fw_data st size:%d", __func__, sizeof_frc_fw_data_struct());
frc_devp->out_line = frc_init_out_line();
ret = alloc_chrdev_region(&frc_devp->devno, 0, FRC_DEVNO, FRC_NAME);
if (ret < 0) {
PR_ERR("%s: alloc region fail\n", __func__);
goto fail_alloc_region;
}
frc_devp->clsp = class_create(THIS_MODULE, FRC_CLASS_NAME);
if (IS_ERR(frc_devp->clsp)) {
ret = PTR_ERR(frc_devp->clsp);
PR_ERR("%s: create class fail\n", __func__);
goto fail_create_cls;
}
for (i = 0; frc_class_attrs[i].attr.name; i++) {
if (class_create_file(frc_devp->clsp, &frc_class_attrs[i]) < 0)
goto fail_class_create_file;
}
get_vout_info(frc_devp);
cdev_init(&frc_devp->cdev, &frc_fops);
frc_devp->cdev.owner = THIS_MODULE;
ret = cdev_add(&frc_devp->cdev, frc_devp->devno, FRC_DEVNO);
if (ret)
goto fail_add_cdev;
frc_devp->dev = device_create(frc_devp->clsp, NULL,
frc_devp->devno, frc_devp, FRC_NAME);
if (IS_ERR(frc_devp->dev)) {
PR_ERR("%s: device create fail\n", __func__);
goto fail_dev_create;
}
dev_set_drvdata(frc_devp->dev, frc_devp);
platform_set_drvdata(pdev, frc_devp);
frc_devp->pdev = pdev;
frc_data = (struct frc_data_s *)frc_devp->data;
// fw_data = (struct frc_fw_data_s *)frc_devp->fw_data;
frc_dts_parse(frc_devp);
// if (ret < 0) // fixed CID 139501
// goto fail_dev_create;
tasklet_init(&frc_devp->input_tasklet, frc_input_tasklet_pro, (unsigned long)frc_devp);
tasklet_init(&frc_devp->output_tasklet, frc_output_tasklet_pro, (unsigned long)frc_devp);
/*register a notify*/
vout_register_client(&frc_notifier_nb);
vd_signal_register_client(&frc_notifier_vb);
/*driver internal data initial*/
frc_drv_initial(frc_devp);
frc_clk_init(frc_devp);
frc_devp->power_on_flag = true;
frc_init_config(frc_devp);
/*buffer config*/
frc_buf_calculate(frc_devp);
frc_buf_alloc(frc_devp);
frc_buf_distribute(frc_devp);
frc_buf_config(frc_devp);
frc_hw_initial(frc_devp);
frc_internal_initial(frc_devp); /*need after frc_top_init*/
/*enable irq*/
if (frc_devp->in_irq > 0)
enable_irq(frc_devp->in_irq);
if (frc_devp->out_irq > 0)
enable_irq(frc_devp->out_irq);
#ifdef CONFIG_AMLOGIC_MEDIA_FRC_RDMA
if (frc_devp->rdma_irq > 0)
enable_irq(frc_devp->rdma_irq);
#endif
INIT_WORK(&frc_devp->frc_clk_work, frc_clock_workaround);
frc_devp->probe_ok = true;
frc_devp->power_off_flag = false;
PR_FRC("%s probe st:%d", __func__, frc_devp->probe_ok);
return ret;
fail_dev_create:
cdev_del(&frc_devp->cdev);
fail_add_cdev:
PR_ERR("%s: cdev add fail\n", __func__);
fail_class_create_file:
for (i = 0; frc_class_attrs[i].attr.name; i++)
class_remove_file(frc_devp->clsp, &frc_class_attrs[i]);
class_destroy(frc_devp->clsp);
fail_create_cls:
unregister_chrdev_region(frc_devp->devno, FRC_DEVNO);
fail_alloc_region:
// kfree(frc_dev->fw_data);
frc_devp->fw_data = NULL;
fail_alloc_fw_data_fail:
kfree(frc_devp->data);
frc_devp->data = NULL;
fail_alloc_data_fail:
// kfree(frc_dev);
// frc_dev = NULL;
// fail_alloc_dev:
return ret;
}
static int __exit frc_remove(struct platform_device *pdev)
{
struct frc_dev_s *frc_devp = &frc_dev;
PR_FRC("%s:module remove\n", __func__);
// frc_devp = platform_get_drvdata(pdev);
cancel_work_sync(&frc_devp->frc_clk_work);
tasklet_kill(&frc_devp->input_tasklet);
tasklet_kill(&frc_devp->output_tasklet);
tasklet_disable(&frc_devp->input_tasklet);
tasklet_disable(&frc_devp->output_tasklet);
if (frc_devp->in_irq > 0)
free_irq(frc_devp->in_irq, (void *)frc_devp);
if (frc_devp->out_irq > 0)
free_irq(frc_devp->out_irq, (void *)frc_devp);
device_destroy(frc_devp->clsp, frc_devp->devno);
cdev_del(&frc_devp->cdev);
class_destroy(frc_devp->clsp);
unregister_chrdev_region(frc_devp->devno, FRC_DEVNO);
//destroy_workqueue(frc_devp->frc_wq);
set_frc_clk_disable();
//if (frc_devp->dbg_buf)
// kfree(frc_devp->dbg_buf);
frc_buf_release(frc_devp);
kfree(frc_devp->data);
frc_devp->data = NULL;
// kfree(frc_dev);
// frc_dev = NULL;
PR_FRC("%s:module remove done\n", __func__);
return 0;
}
static void frc_shutdown(struct platform_device *pdev)
{
struct frc_dev_s *frc_devp = &frc_dev;
PR_FRC("%s:module shutdown\n", __func__);
// frc_devp = platform_get_drvdata(pdev);
frc_devp->power_on_flag = false;
tasklet_kill(&frc_devp->input_tasklet);
tasklet_kill(&frc_devp->output_tasklet);
tasklet_disable(&frc_devp->input_tasklet);
tasklet_disable(&frc_devp->output_tasklet);
if (frc_devp->in_irq > 0)
free_irq(frc_devp->in_irq, (void *)frc_devp);
if (frc_devp->out_irq > 0)
free_irq(frc_devp->out_irq, (void *)frc_devp);
device_destroy(frc_devp->clsp, frc_devp->devno);
cdev_del(&frc_devp->cdev);
class_destroy(frc_devp->clsp);
unregister_chrdev_region(frc_devp->devno, FRC_DEVNO);
//destroy_workqueue(frc_devp->frc_wq);
set_frc_clk_disable();
//if (frc_devp->dbg_buf)
// kfree(frc_devp->dbg_buf);
frc_buf_release(frc_devp);
kfree(frc_devp->data);
frc_devp->data = NULL;
// kfree(frc_dev);
// frc_dev = NULL;
pwr_ctrl_psci_smc(PDID_T3_FRCTOP, PWR_OFF);
pwr_ctrl_psci_smc(PDID_T3_FRCME, PWR_OFF);
pwr_ctrl_psci_smc(PDID_T3_FRCMC, PWR_OFF);
PR_FRC("%s:module shutdown done with powerdomain\n", __func__);
}
#if CONFIG_PM
static int frc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct frc_dev_s *devp = NULL;
devp = get_frc_devp();
if (!devp)
return -1;
PR_FRC("%s ...\n", __func__);
frc_power_domain_ctrl(devp, 0);
if (devp->power_on_flag)
devp->power_on_flag = false;
return 0;
}
static int frc_resume(struct platform_device *pdev)
{
struct frc_dev_s *devp = NULL;
devp = get_frc_devp();
if (!devp)
return -1;
PR_FRC("%s ...\n", __func__);
frc_power_domain_ctrl(devp, 1);
if (!devp->power_on_flag)
devp->power_on_flag = true;
return 0;
}
#endif
static struct platform_driver frc_driver = {
.probe = frc_probe,
.remove = frc_remove,
.shutdown = frc_shutdown,
#ifdef CONFIG_PM
.suspend = frc_suspend,
.resume = frc_resume,
#endif
.driver = {
.name = "aml_frc",
.owner = THIS_MODULE,
.of_match_table = frc_dts_match,
},
};
int __init frc_init(void)
{
PR_FRC("%s:module init\n", __func__);
if (platform_driver_register(&frc_driver)) {
PR_ERR("failed to register frc driver module\n");
return -ENODEV;
}
return 0;
}
void __exit frc_exit(void)
{
platform_driver_unregister(&frc_driver);
PR_FRC("%s:module exit\n", __func__);
}