blob: ec79ae55b105e8d5dec6b648b50c952e0035f45e [file]
// 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/miscdevice.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 <linux/amlogic/media/resource_mgr/resourcemanage.h>
#include <linux/amlogic/aml_ddr_tool.h>
#include <dt-bindings/power/t3-pd.h>
#include <dt-bindings/power/t5m-pd.h>
#include <dt-bindings/power/t3x-pd.h>
#include <linux/amlogic/media/video_sink/video_signal_notify.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/dma-map-ops.h>
#include <linux/cma.h>
#include <linux/genalloc.h>
#include <linux/dma-mapping.h>
#include <linux/timer.h>
#include <linux/hrtimer.h>
#include "frc_drv.h"
#include "frc_proc.h"
#include "frc_dbg.h"
#include "frc_buf.h"
#include "frc_hw.h"
#include "frc_memc_dbg.h"
// #ifdef CONFIG_AMLOGIC_MEDIA_FRC_RDMA
#include "frc_rdma.h"
// #endif
const struct frm_dly_dat_s chip_frc_frame_dly[3][4] = {
{ // chip_t3 fhd,4k2k,4k1k,other
{130, 11},
{222, 28},
{222, 20},
{140, 10},
},
{ // chip_t5m fhd,4k2k,4k1k,other
{110, 5},
{222, 28},
{222, 20},
{140, 10},
},
{ // chip_t3x fhd,4k2k,4k1k,4K2K-120Hz
{130, 11},
{120, 14},
{232, 22}, //{266, 28},
{220, 20}, // {240, 15},
},
};
struct hrtimer frc_hi_timer; // timer
// 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;
struct platform_device *runtime_frc_dev;
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_def_ver[] = "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);
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(buf, 0644, frc_buf_show, frc_buf_store),
__ATTR(rdma, 0644, frc_rdma_show, frc_rdma_store),
__ATTR(param, 0644, frc_param_show, frc_param_store),
__ATTR(other, 0644, frc_other_show, frc_other_store),
__ATTR(bbd_ctrl_param, 0644, frc_bbd_ctrl_param_show, frc_bbd_ctrl_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(scene_chg_detect_param, 0644, frc_scene_chg_detect_param_show,
frc_scene_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(mc_ctrl_param, 0644, frc_mc_ctrl_param_show, frc_mc_ctrl_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(glb_ctrl_param, 0644, frc_glb_ctrl_param_show, frc_glb_ctrl_param_store),
__ATTR(bad_edit_ctrl_param, 0644, frc_bad_edit_ctrl_param_show,
frc_bad_edit_ctrl_param_store),
__ATTR(region_fb_ctrl_param, 0644, frc_region_fb_ctrl_param_show,
frc_region_fb_ctrl_param_store),
__ATTR(trace_enable, 0664,
frc_rdma_trace_enable_show, frc_rdma_trace_enable_stroe),
__ATTR(trace_reg, 0664,
frc_rdma_trace_reg_show, frc_rdma_trace_reg_stroe),
__ATTR(probe, 0664, frc_probe_dbg_show, frc_probe_dbg_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];
enum frc_fpp_state_e fpp_state;
struct v4l2_ext_memc_motion_comp_info comp_info;
devp = file->private_data;
if (!devp)
return -EFAULT;
if (!devp->probe_ok)
return -EFAULT;
if (frc_dbg_ctrl) {
pr_frc(0, "return frc ioc\n");
return 0;
}
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_DEBLUR_LEVEL:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
frc_memc_set_deblur(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 boot_timestamp_en%d boot_check%d\n",
data, devp->in_sts.boot_timestamp_en, devp->in_sts.boot_check_finished);
if (data) {
// if (devp->in_sts.boot_timestamp_en &&
// !devp->in_sts.boot_check_finished) {
// devp->in_sts.auto_ctrl_reserved = 1;
// devp->frc_sts.auto_ctrl = false;
// frc_change_to_state(FRC_STATE_DISABLE);
// pr_frc(1, "set memc_autoctrl-1:%d\n", data);
// } else if (!devp->frc_sts.auto_ctrl) {
if (!devp->frc_sts.auto_ctrl) {
devp->frc_sts.auto_ctrl = true;
// devp->frc_sts.re_config = true;
frc_change_to_state(FRC_STATE_ENABLE);
pr_frc(1, "set memc_autoctrl-2:%d\n", data);
}
} else {
devp->frc_sts.auto_ctrl = false;
//if (devp->frc_sts.state != FRC_STATE_BYPASS)
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_SET_FPP_MEMC_LEVEL:
if (copy_from_user(&fpp_state, argp,
sizeof(enum frc_fpp_state_e))) {
pr_frc(1, "fpp copy from user error!/n");
ret = -EFAULT;
break;
}
if (fpp_state == FPP_MEMC_OFF)
frc_fpp_memc_set_level(0, 0);
else if (fpp_state == FPP_MEMC_LOW)
frc_fpp_memc_set_level(9, 0);
else if (fpp_state == FPP_MEMC_MID)
frc_fpp_memc_set_level(10, 0);
else if (fpp_state == FPP_MEMC_HIGH)
frc_fpp_memc_set_level(10, 1);
else if (fpp_state == FPP_MEMC_24PFILM)
frc_fpp_memc_set_level(10, 2);
else
frc_fpp_memc_set_level((u8)fpp_state, 0);
pr_frc(1, "SET_FPP_MEMC_LEVEL:%d\n", fpp_state);
break;
case FRC_IOC_SET_MEMC_VENDOR:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
frc_tell_alg_vendor(data);
break;
case FRC_IOC_SET_MEMC_FB:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
frc_set_memc_fallback(data);
break;
case FRC_IOC_SET_MEMC_FILM:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
frc_set_film_support(data);
break;
case FRC_IOC_SET_MEMC_N2M:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
if (devp->auto_n2m == 0)
frc_set_n2m(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;
case PQ_MEMC_IOC_SET_LGE_MEMC_LEVEL:
if (copy_from_user(&comp_info, argp,
sizeof(struct v4l2_ext_memc_motion_comp_info))) {
pr_frc(1, "lge copy from user error!/n");
ret = -EFAULT;
break;
}
// parm1 control memc level, parm2 control fullback reserved
frc_lge_memc_set_level(comp_info);
pr_frc(1, "SET_LGE_MEMC_LEVEL\n");
break;
case PQ_MEMC_IOC_GET_LGE_MEMC_LEVEL:
frc_lge_memc_get_level(&comp_info);
if (copy_to_user(argp, &comp_info,
sizeof(struct v4l2_ext_memc_motion_comp_info))) {
pr_frc(0, "lge copy from user error!/n");
ret = -EFAULT;
break;
}
pr_frc(1, "GET_LGE_MEMC_LEVEL:%d\n", comp_info.memc_type);
break;
case PQ_MEMC_IOC_LGE_SET_MEMC_INIT:
if (copy_from_user(&data, argp, sizeof(u32))) {
ret = -EFAULT;
break;
}
pr_frc(1, "parm:%d\n", data);
frc_lge_memc_init();
break;
}
return ret;
}
static const struct dts_match_data dts_match_t3 = {
.chip = ID_T3,
};
static const struct dts_match_data dts_match_t5m = {
.chip = ID_T5M,
};
static const struct dts_match_data dts_match_t3x = {
.chip = ID_T3X,
};
static const struct of_device_id frc_dts_match[] = {
{
.compatible = "amlogic, t3_frc",
.data = &dts_match_t3,
},
{
.compatible = "amlogic, t5m_frc",
.data = &dts_match_t5m,
},
{
.compatible = "amlogic, t3x_frc",
.data = &dts_match_t3x,
},
{},
};
int frc_attach_pd(struct frc_dev_s *devp)
{
enum chip_id chip;
struct device_link *link;
int i;
u32 pd_cnt;
char *pd_name[3] = {"frc-top", "frc-me", "frc-mc"};
struct platform_device *pdev = devp->pdev;
chip = get_chip_type();
if (chip == ID_T5M || chip == ID_T3X)
return 0;
if (pdev->dev.pm_domain) {
pr_frc(0, "%s err pm domain\n", __func__);
return -1;
}
pd_cnt = 3;
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);
set_frc_clk_disable(devp, 1);
devp->power_on_flag = false;
}
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;
if (devp->clk_me || devp->clk_frc) {
set_frc_clk_disable(devp, 0);
} else {
devp->clk_frc = clk_get(&devp->pdev->dev, "clk_frc");
devp->clk_me = clk_get(&devp->pdev->dev, "clk_me");
frc_clk_init(devp);
}
// alloc frc buf according to status of alloced
if (!devp->buf.cma_mem_alloced) {
frc_buf_alloc(devp);
}
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(2, "t3 power domain power %d\n", onoff);
} else if (chip == ID_T5M) {
if (onoff) {
#ifdef K_MEMC_CLK_DIS
pwr_ctrl_psci_smc(PDID_T5M_FRC_TOP, PWR_ON);
#endif
devp->power_on_flag = true;
pr_frc(0, "%s clk set\n", __func__);
if (devp->clk_me || devp->clk_frc) {
set_frc_clk_disable(devp, 0);
} else {
devp->clk_frc = clk_get(&devp->pdev->dev, "clk_frc");
devp->clk_me = clk_get(&devp->pdev->dev, "clk_me");
frc_clk_init(devp);
}
if (!devp->buf.cma_mem_alloced)
frc_buf_alloc(devp);
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_T5M_FRC_TOP, PWR_OFF);
#endif
}
pr_frc(2, "t5m power domain power. %d\n", onoff);
} else if (chip == ID_T3X) {
if (onoff) {
#ifdef K_MEMC_CLK_DIS
pwr_ctrl_psci_smc(PDID_T3X_FRC_TOP, PWR_ON);
#endif
devp->power_on_flag = true;
if (devp->clk_me || devp->clk_frc) {
set_frc_clk_disable(devp, 0);
} else {
devp->clk_frc = clk_get(&devp->pdev->dev, "clk_frc");
devp->clk_me = clk_get(&devp->pdev->dev, "clk_me");
frc_clk_init(devp);
}
if (!devp->buf.cma_mem_alloced)
frc_buf_alloc(devp);
set_frc_clk_disable(devp, 0);
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 {
set_frc_clk_disable(devp, 1);
#ifdef K_MEMC_CLK_DIS
pwr_ctrl_psci_smc(PDID_T3X_FRC_TOP, PWR_OFF);
#endif
}
pr_frc(2, "t3x 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;
struct device_node *np = pdev->dev.of_node;
struct frc_fw_data_s *pfw_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;
pfw_data = (struct frc_fw_data_s *)frc_devp->fw_data;
frc_data->match_data = of_id->data;
PR_FRC("%s\tchip id:%d\n", of_id->compatible, frc_data->match_data->chip);
pfw_data->frc_top_type.chip = (u8)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->axi_crash_irq = of_irq_get_byname(of_node, "irq_axi_crash");
snprintf(frc_devp->axi_crash_irq_name,
sizeof(frc_devp->axi_crash_irq_name), "axi_crash_irq");
// PR_FRC("%s=%d\n", frc_devp->axi_crash_irq_name, frc_devp->axi_crash_irq);
if (frc_devp->axi_crash_irq > 0) {
ret = request_irq(frc_devp->axi_crash_irq, frc_axi_crash_isr, IRQF_SHARED,
frc_devp->axi_crash_irq_name, (void *)frc_devp);
if (ret)
PR_ERR("request axi_crash irq fail\n");
else
disable_irq(frc_devp->axi_crash_irq);
} else {
PR_ERR("axi_crash irq is not enabled\n");
}
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\t%s=%d\t%s=%d\t%s=%d\n", frc_devp->in_irq_name, frc_devp->in_irq,
frc_devp->out_irq_name, frc_devp->out_irq,
frc_devp->axi_crash_irq_name, frc_devp->axi_crash_irq,
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;
}
} 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;
}
} 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;
}
} else {
frc_devp->vpu_reg = NULL;
vpu_base = NULL;
}
// frc buf reserved
ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0);
if (ret) {
pr_frc(0, "cma resource undefined !\n");
frc_devp->buf.cma_mem_size = 0;
} else {
frc_devp->buf.cma_mem_size = dma_get_cma_size_int_byte(&pdev->dev);
}
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;
if (devp->clk_state == FRC_CLOCK_OFF)
return 0;
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;
enum chip_id chip = get_chip_type();
u32 flags;
if (!devp)
return -1;
if (devp->clk_state == FRC_CLOCK_OFF)
return -1;
if (chip == ID_T3X)
return 0;
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 && (!devp->in_sts.frc_seamless_en ||
(devp->in_sts.frc_seamless_en && devp->in_sts.frc_is_tvin))) {
pr_frc(0, "%s start disable frc", __func__);
set_frc_enable(false);
set_frc_bypass(true);
// frc_change_to_state(FRC_STATE_DISABLE);
frc_change_to_state(FRC_STATE_BYPASS);
frc_state_change_finish(devp);
if (devp->frc_sts.frame_cnt != 0) {
devp->frc_sts.frame_cnt = 0;
pr_frc(1, "%s reset frm_cnt\n", __func__);
}
pr_frc(1, "%s VIDEO_SIZE_CHANGE_EVENT\n", __func__);
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 int runtimepm_frc_open(struct inode *inode, struct file *file)
{
pm_runtime_get_sync(&runtime_frc_dev->dev);
return 0;
}
static int runtimepm_frc_release(struct inode *inode, struct file *file)
{
pm_runtime_put_sync(&runtime_frc_dev->dev);
return 0;
}
static const struct file_operations runtimepm_frc_fops = {
.owner = THIS_MODULE,
.open = runtimepm_frc_open,
.release = runtimepm_frc_release,
};
static struct miscdevice frc_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "runtime_frc",
.fops = &runtimepm_frc_fops,
};
/****************************************************************************/
#if IS_ENABLED(CONFIG_AMLOGIC_DMC_DEV_ACCESS)
static int frc0_dmc_dev_access_notify(struct notifier_block *nb, unsigned long id, void *data)
{
struct dmc_dev_access_data *dmc = (struct dmc_dev_access_data *)data;
struct frc_dev_s *devp = get_frc_devp();
if (devp->dmc_cfg[0].id == id) {
devp->dmc_cfg[0].ddr_addr = dmc->addr;
devp->dmc_cfg[0].ddr_size = dmc->size;
pr_frc(0, "%s:id(%d)-trust handle addr:0x%lX,size:0x%lX\n",
__func__, (int)id, dmc->addr, dmc->size);
}
return 0;
}
static int frc1_dmc_dev_access_notify(struct notifier_block *nb, unsigned long id, void *data)
{
struct dmc_dev_access_data *dmc = (struct dmc_dev_access_data *)data;
struct frc_dev_s *devp = get_frc_devp();
if (devp->dmc_cfg[1].id == id) {
devp->dmc_cfg[1].ddr_addr = dmc->addr;
devp->dmc_cfg[1].ddr_size = dmc->size;
pr_frc(0, "%s:id(%d)--trust handle addr:0x%lX,size:0x%lX\n",
__func__, (int)id, dmc->addr, dmc->size);
}
return 0;
}
static int frc2_dmc_dev_access_notify(struct notifier_block *nb, unsigned long id, void *data)
{
struct dmc_dev_access_data *dmc = (struct dmc_dev_access_data *)data;
struct frc_dev_s *devp = get_frc_devp();
if (devp->dmc_cfg[2].id == id) {
devp->dmc_cfg[2].ddr_addr = dmc->addr;
devp->dmc_cfg[2].ddr_size = dmc->size;
pr_frc(0, "%s:id(%d)-trust handle addr:0x%lX,size:0x%lX\n",
__func__, (int)id, dmc->addr, dmc->size);
}
return 0;
}
static struct notifier_block frc0_dmc_dev_access_nb = {
.notifier_call = frc0_dmc_dev_access_notify,
};
static struct notifier_block frc1_dmc_dev_access_nb = {
.notifier_call = frc1_dmc_dev_access_notify,
};
static struct notifier_block frc2_dmc_dev_access_nb = {
.notifier_call = frc2_dmc_dev_access_notify,
};
void frc_dmc_notifier(void)
{
struct frc_dev_s *devp = get_frc_devp();
if (unlikely(!devp)) {
PR_ERR("%s:devp is NULL\n", __func__);
return;
}
devp->dmc_cfg[0].id = register_dmc_dev_access_notifier("FRC0",
&frc0_dmc_dev_access_nb);
devp->dmc_cfg[1].id = register_dmc_dev_access_notifier("FRC1",
&frc1_dmc_dev_access_nb);
devp->dmc_cfg[2].id = register_dmc_dev_access_notifier("FRC2",
&frc2_dmc_dev_access_nb);
}
//unregister notifier
void frc_dmc_un_notifier(void)
{
struct frc_dev_s *devp = get_frc_devp();
if (unlikely(!devp)) {
PR_ERR("%s:devp is NULL\n", __func__);
return;
}
devp->dmc_cfg[0].id = unregister_dmc_dev_access_notifier("FRC0",
&frc0_dmc_dev_access_nb);
devp->dmc_cfg[0].id = unregister_dmc_dev_access_notifier("FRC1",
&frc1_dmc_dev_access_nb);
devp->dmc_cfg[0].id = unregister_dmc_dev_access_notifier("FRC2",
&frc2_dmc_dev_access_nb);
}
#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);
int default_me_clk, default_mc_clk;
int default_min_me_clk, default_min_mc_clk;
if (unlikely(!devp)) {
PR_ERR("%s err, devp is NULL\n", __func__);
return;
}
if (!devp->probe_ok)
return;
if (!devp->power_on_flag)
return;
if (get_chip_type() == ID_T3X) {
default_min_me_clk = FRC_CLOCK_RATE_333;
default_min_mc_clk = FRC_CLOCK_RATE_333;
default_me_clk = FRC_CLOCK_RATE_667;
default_mc_clk = FRC_CLOCK_RATE_667;
} else {
default_min_me_clk = FRC_CLOCK_RATE_333;
default_min_mc_clk = FRC_CLOCK_RATE_333;
default_me_clk = FRC_CLOCK_RATE_333;
default_mc_clk = FRC_CLOCK_RATE_667;
}
if (devp->clk_chg == FRC_CLOCK_FIXED) {
clk_set_rate(devp->clk_me, default_me_clk);
clk_set_rate(devp->clk_frc, default_mc_clk);
if (devp->clk_state != FRC_CLOCK_OFF)
set_frc_clk_disable(devp, 1);
if (devp->clk_state != FRC_CLOCK_NOR)
set_frc_clk_disable(devp, 0);
} else if (devp->clk_chg == FRC_CLOCK_DYNAMIC_0) {
if (devp->clk_state == FRC_CLOCK_XXX2NOR)
devp->clk_state = FRC_CLOCK_OFF2NOR;
else if (devp->clk_state == FRC_CLOCK_NOR2XXX)
devp->clk_state = FRC_CLOCK_NOR2OFF;
} else if (devp->clk_chg == FRC_CLOCK_DYNAMIC_1) {
if (devp->clk_state == FRC_CLOCK_XXX2NOR)
devp->clk_state = FRC_CLOCK_MIN2NOR;
else if (devp->clk_state == FRC_CLOCK_NOR2XXX)
devp->clk_state = FRC_CLOCK_NOR2MIN;
}
if (devp->clk_state == FRC_CLOCK_NOR2OFF) {
set_frc_clk_disable(devp, 1);
} else if (devp->clk_state == FRC_CLOCK_OFF2NOR) {
set_frc_clk_disable(devp, 0);
} else if (devp->clk_state == FRC_CLOCK_NOR2MIN) {
clk_set_rate(devp->clk_me, default_min_me_clk);
clk_set_rate(devp->clk_frc, default_min_mc_clk);
devp->clk_state = FRC_CLOCK_MIN;
} else if (devp->clk_state == FRC_CLOCK_MIN2NOR) {
clk_set_rate(devp->clk_me, default_me_clk);
clk_set_rate(devp->clk_frc, default_mc_clk);
devp->clk_state = FRC_CLOCK_NOR;
} else if (devp->clk_state == FRC_CLOCK_MIN2OFF) {
set_frc_clk_disable(devp, 1);
devp->clk_state = FRC_CLOCK_OFF;
} else if (devp->clk_state == FRC_CLOCK_OFF2MIN) {
clk_set_rate(devp->clk_me, default_min_me_clk);
clk_set_rate(devp->clk_frc, default_min_mc_clk);
set_frc_clk_disable(devp, 0);
devp->clk_state = FRC_CLOCK_MIN;
}
pr_frc(1, "%s, clk_new state:%d\n", __func__, devp->clk_state);
}
static void frc_secure_workaround(struct work_struct *work)
{
struct frc_dev_s *devp = container_of(work,
struct frc_dev_s, frc_secure_work);
if (unlikely(!devp)) {
PR_ERR("%s err, devp is NULL\n", __func__);
return;
}
frc_mm_secure_set(devp);
}
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;
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.changed_flag = 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->dbg_force_en = 0;
devp->other1_flag = 0;
devp->other2_flag = 0; // 25, 16;
devp->vlock_flag = 1;
devp->dbg_mvrd_mode = 8;
devp->dbg_mute_disable = 1;
devp->test2 = 1;
/*input sts initial*/
devp->in_sts.have_vf_cnt = 0;
devp->in_sts.no_vf_cnt = 0;
devp->in_sts.vf_sts = 0;/*initial to no*/
devp->dbg_in_out_ratio = FRC_RATIO_1_1;
// devp->dbg_in_out_ratio = FRC_RATIO_2_5;
// devp->dbg_in_out_ratio = FRC_RATIO_1_2;
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->prot_mode = true;
devp->in_out_ratio = FRC_RATIO_1_1;
// devp->in_out_ratio = FRC_RATIO_2_5;
// devp->in_out_ratio = FRC_RATIO_1_2;
// devp->film_mode = EN_DRV_FILM32;
devp->film_mode = EN_DRV_VIDEO;
devp->film_mode_det = 0;
devp->pat_dbg.pat_en = 1;
// test init
devp->in_sts.t3x_adj_mcdw_hv = 2;
// ctrl high-priority tasklet
devp->in_sts.hi_en = 0;
devp->out_sts.hi_en = 0;
devp->task_run_method = MEMC_RUN_IN_IRQ;
// devp->task_run_method = MEMC_ALG_PASS;
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 = 1;
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*/
fw_data->frc_top_type.motion_ctrl = RD_MOTION_BY_VPU_ISR;
for (i = 0; i < RD_REG_MAX; i++)
fw_data->reg_val[i].addr = 0x0;
if (fw_data->frc_top_type.chip != 0)
memcpy(&devp->frm_dly_set[0],
&chip_frc_frame_dly[fw_data->frc_top_type.chip - 1][0],
sizeof(struct frm_dly_dat_s) * 4);
else
memcpy(&devp->frm_dly_set[0],
&chip_frc_frame_dly[0][0],
sizeof(struct frm_dly_dat_s) * 4);
pr_frc(0, "frc_get_dly:%d,%d, %d,%d, %d,%d, %d,%d\n",
devp->frm_dly_set[0].mevp_frm_dly,
devp->frm_dly_set[0].mc_frm_dly,
devp->frm_dly_set[1].mevp_frm_dly,
devp->frm_dly_set[1].mc_frm_dly,
devp->frm_dly_set[2].mevp_frm_dly,
devp->frm_dly_set[2].mc_frm_dly,
devp->frm_dly_set[3].mevp_frm_dly,
devp->frm_dly_set[3].mc_frm_dly);
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));
devp->ud_dbg.res2_dbg_en = 3; // t3x_revB test
devp->ud_dbg.align_dbg_en = 0; // t3x_revB test
if (get_chip_type() == ID_T3X) {
devp->in_sts.boot_timestamp_en = 1;
devp->vpu_byp_frc_reg_addr = VIU_FRC_MISC;
devp->auto_n2m = 1;
devp->use_pre_vsync = PRE_VSYNC_120HZ;
} else if (get_chip_type() == ID_T5M) {
devp->vpu_byp_frc_reg_addr = VPU_FRC_TOP_CTRL;
devp->auto_n2m = 1;
devp->use_pre_vsync = PRE_VSYNC_120HZ;
} else if (get_chip_type() == ID_T3) {
devp->vpu_byp_frc_reg_addr = VPU_FRC_TOP_CTRL;
devp->auto_n2m = 0;
devp->use_pre_vsync = PRE_VSYNC_NONE;
} else {
devp->vpu_byp_frc_reg_addr = VPU_FRC_TOP_CTRL;
devp->auto_n2m = 0;
devp->use_pre_vsync = PRE_VSYNC_NONE;
}
}
void get_vout_info(struct frc_dev_s *frc_devp)
{
struct vinfo_s *vinfo = get_current_vinfo();
struct frc_fw_data_s *pfw_data;
u16 tmpframterate = 0;
if (!frc_devp) {
PR_ERR("%s: frc_devp is null\n", __func__);
return;
}
pfw_data = (struct frc_fw_data_s *)frc_devp->fw_data;
if (frc_devp->out_sts.vout_height != vinfo->height)
frc_devp->out_sts.vout_height = vinfo->height;
if (frc_devp->out_sts.vout_width != vinfo->width)
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;
pfw_data->frc_top_type.frc_out_frm_rate =
frc_devp->out_sts.out_framerate;
pfw_data->frc_top_type.frc_other_reserved =
frc_devp->out_sts.out_framerate;
if (frc_devp->auto_n2m == 1) {
if (frc_devp->out_sts.out_framerate > 90) {
frc_set_n2m(FRC_RATIO_1_2);
if ((frc_devp->use_pre_vsync & PRE_VSYNC_120HZ) ==
PRE_VSYNC_120HZ) {
set_vsync_2to1_mode(0);
set_pre_vsync_mode(1);
} else {
set_vsync_2to1_mode(1);
set_pre_vsync_mode(0);
}
} else if (frc_devp->out_sts.out_framerate < 70) {
if ((frc_devp->use_pre_vsync & PRE_VSYNC_060HZ) ==
PRE_VSYNC_060HZ) {
frc_set_n2m(FRC_RATIO_1_2);
set_vsync_2to1_mode(0);
set_pre_vsync_mode(1);
} else {
frc_set_n2m(FRC_RATIO_1_1);
set_vsync_2to1_mode(0);
set_pre_vsync_mode(0);
}
}
}
pr_frc(1, "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);
}
}
int frc_buf_set(struct frc_dev_s *frc_devp)
{
if (!frc_devp) {
PR_ERR("%s: frc_devp is null\n", __func__);
return -1;
}
if (frc_buf_calculate(frc_devp) != 0)
return -1;
if (frc_buf_alloc(frc_devp) != 0)
return -1;
if (frc_buf_distribute(frc_devp) != 0)
return -1;
if (frc_buf_config(frc_devp) != 0)
return -1;
else
return 0;
}
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;
// }
// pr_frc(0, "%s, frc probe start\n", __func__);
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_def_ver[0],
strlen(frc_alg_def_ver));
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());
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(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;
if (frc_dts_parse(frc_devp)) {
PR_ERR("dts parse error\n");
goto fail_dev_create;
}
// 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);
if (frc_data->match_data->chip == ID_T5M)
resman_register_debug_callback(FRC_TITLE, set_frc_config);
/*driver internal data initial*/
frc_drv_initial(frc_devp);
frc_clk_init(frc_devp);
get_vout_info(frc_devp);
frc_devp->power_on_flag = true;
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
PR_ERR("pm_runtime_get_sync error\n");
goto fail_dev_create;
}
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);
if (frc_buf_set(frc_devp) != 0)
goto fail_dev_create;
frc_internal_initial(frc_devp);
frc_devp->out_line = frc_init_out_line();
frc_hw_initial(frc_devp);
/*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);
// if (frc_devp->axi_crash_irq > 0)
// enable_irq(frc_devp->axi_crash_irq);
// #ifdef CONFIG_AMLOGIC_MEDIA_FRC_RDMA
if (frc_devp->rdma_irq > 0)
enable_irq(frc_devp->rdma_irq);
if (!frc_rdma_init())
PR_FRC("%s frc rdma init failed\n", __func__);
// #endif
INIT_WORK(&frc_devp->frc_clk_work, frc_clock_workaround);
INIT_WORK(&frc_devp->frc_print_work, frc_debug_table_print);
INIT_WORK(&frc_devp->frc_secure_work, frc_secure_workaround);
frc_devp->clk_chg = FRC_CLOCK_DYNAMIC_0;
frc_set_enter_forcefilm(frc_devp, 0);
frc_devp->probe_ok = true;
frc_devp->power_off_flag = false;
ret = misc_register(&frc_misc);
runtime_frc_dev = pdev;
pm_runtime_forbid(&pdev->dev);
#if IS_ENABLED(CONFIG_AMLOGIC_DMC_DEV_ACCESS)
frc_dmc_notifier();
#endif
// 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;
if (!frc_devp || !frc_devp->probe_ok)
return -1;
PR_FRC("%s:module remove\n", __func__);
// frc_devp = platform_get_drvdata(pdev);
cancel_work_sync(&frc_devp->frc_clk_work);
cancel_work_sync(&frc_devp->frc_print_work);
cancel_work_sync(&frc_devp->frc_secure_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);
if (frc_devp->axi_crash_irq > 0)
free_irq(frc_devp->axi_crash_irq, (void *)frc_devp);
if (frc_devp->rdma_irq > 0)
free_irq(frc_devp->rdma_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);
set_frc_clk_disable(frc_devp, 1);
frc_buf_release(frc_devp);
kfree(frc_devp->data);
frc_devp->data = NULL;
#if IS_ENABLED(CONFIG_AMLOGIC_DMC_DEV_ACCESS)
frc_dmc_un_notifier();
#endif
// 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;
enum chip_id chip;
if (!frc_devp || !frc_devp->probe_ok)
return;
PR_FRC("%s:module shutdown\n", __func__);
chip = get_chip_type();
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);
if (frc_devp->axi_crash_irq > 0)
free_irq(frc_devp->axi_crash_irq, (void *)frc_devp);
if (frc_devp->rdma_irq > 0)
free_irq(frc_devp->rdma_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);
set_frc_clk_disable(frc_devp, 1);
frc_buf_release(frc_devp);
kfree(frc_devp->data);
frc_devp->data = NULL;
// kfree(frc_dev);
// frc_dev = NULL;
#if IS_ENABLED(CONFIG_AMLOGIC_DMC_DEV_ACCESS)
frc_dmc_un_notifier();
#endif
if (chip == ID_T3) {
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);
} else if (chip == ID_T5M) {
pwr_ctrl_psci_smc(PDID_T5M_FRC_TOP, PWR_OFF);
} else if (chip == ID_T3X) {
pwr_ctrl_psci_smc(PDID_T3X_FRC_TOP, 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 || !devp->probe_ok)
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 || !devp->probe_ok)
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;
}
static int frc_pm_suspend(struct device *dev)
{
struct frc_dev_s *devp = NULL;
devp = get_frc_devp();
if (!devp)
return -1;
PR_FRC("call %s ...\n", __func__);
frc_power_domain_ctrl(devp, 0);
if (devp->power_on_flag)
devp->power_on_flag = false;
return 0;
}
static int frc_pm_resume(struct device *dev)
{
struct frc_dev_s *devp = NULL;
devp = get_frc_devp();
if (!devp)
return -1;
PR_FRC("call %s ...\n", __func__);
frc_power_domain_ctrl(devp, 1);
if (!devp->power_on_flag)
devp->power_on_flag = true;
set_frc_bypass(ON);
devp->frc_sts.auto_ctrl = true;
devp->frc_sts.re_config = true;
return 0;
}
static int frc_runtime_suspend(struct device *dev)
{
struct frc_dev_s *devp = NULL;
devp = get_frc_devp();
if (!devp)
return -1;
devp->frc_sts.auto_ctrl = false;
PR_FRC("call %s\n", __func__);
frc_power_domain_ctrl(devp, 0);
if (devp->power_on_flag)
devp->power_on_flag = false;
return 0;
}
static int frc_runtime_resume(struct device *dev)
{
struct frc_dev_s *devp = NULL;
devp = get_frc_devp();
if (!devp)
return -1;
// PR_FRC("call %s\n", __func__);
frc_power_domain_ctrl(devp, 1);
if (!devp->power_on_flag)
devp->power_on_flag = true;
set_frc_bypass(ON);
devp->frc_sts.auto_ctrl = true;
devp->frc_sts.re_config = true;
return 0;
}
#endif
static const struct dev_pm_ops frc_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(frc_pm_suspend, frc_pm_resume)
SET_RUNTIME_PM_OPS(frc_runtime_suspend, frc_runtime_resume,
NULL)
};
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,
#ifdef CONFIG_PM
.pm = &frc_dev_pm_ops,
#endif
},
};
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;
}
hrtimer_init(&frc_hi_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
return 0;
}
void __exit frc_exit(void)
{
platform_driver_unregister(&frc_driver);
hrtimer_cancel(&frc_hi_timer);
PR_FRC("%s:module exit\n", __func__);
}