unplugged-kernel/drivers/misc/mediatek/video/mt6885/dispsys/ddp_irq.c

606 lines
16 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#define LOG_TAG "IRQ"
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/timer.h>
#include <linux/sched/clock.h>
#if defined(CONFIG_MTK_SMI_EXT)
#include <smi_public.h>
#endif
/* #include <mach/mt_irq.h> */
#include "disp_drv_platform.h" /* must be at the top-most */
#include "ddp_log.h"
#include "ddp_debug.h"
#include "ddp_reg.h"
#include "ddp_irq.h"
#include "ddp_aal.h"
#include "ddp_drv.h"
#include "disp_helper.h"
#include "ddp_dsi.h"
#include "ddp_postmask.h"
#include "disp_drv_log.h"
#include "primary_display.h"
//#include "smi_debug.h"
#include "disp_lowpower.h"
#include "layering_rule.h"
#include <asm/arch_timer.h>
/* IRQ log print kthread */
static struct task_struct *disp_irq_log_task;
static wait_queue_head_t disp_irq_log_wq;
static unsigned long disp_irq_log_module;
static int disp_irq_rdma_underflow;
static int irq_init;
static unsigned int cnt_rdma_underflow[2];
static unsigned int cnt_rdma_abnormal[2];
static unsigned int cnt_ovl_underflow[OVL_NUM];
static unsigned int cnt_wdma_underflow[2];
static unsigned int cnt_postmask_abnormal;
static unsigned int cnt_postmask_underflow;
unsigned long long rdma_start_time[2] = { 0 };
unsigned long long rdma_end_time[2] = { 0 };
#define DISP_MAX_IRQ_CALLBACK 10
static DDP_IRQ_CALLBACK
irq_module_callback_table[DISP_MODULE_NUM][DISP_MAX_IRQ_CALLBACK];
static DDP_IRQ_CALLBACK irq_callback_table[DISP_MAX_IRQ_CALLBACK];
atomic_t ESDCheck_byCPU = ATOMIC_INIT(0);
/* dsi read by cpu should keep esd_check_bycmdq = 0. */
/* dsi read by cmdq should keep esd_check_bycmdq = 1. */
static atomic_t esd_check_bycmdq = ATOMIC_INIT(1);
void disp_irq_esd_cust_bycmdq(int enable)
{
atomic_set(&esd_check_bycmdq, enable);
}
int disp_irq_esd_cust_get(void)
{
return atomic_read(&esd_check_bycmdq);
}
int disp_register_irq_callback(DDP_IRQ_CALLBACK cb)
{
int i = 0;
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_callback_table[i] == cb)
break;
}
if (i < DISP_MAX_IRQ_CALLBACK)
return 0;
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_callback_table[i] == NULL)
break;
}
if (i == DISP_MAX_IRQ_CALLBACK) {
DDPERR("not enough irq callback entries for module\n");
return -1;
}
DDPMSG("register callback on %d\n", i);
irq_callback_table[i] = cb;
return 0;
}
int disp_unregister_irq_callback(DDP_IRQ_CALLBACK cb)
{
int i;
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_callback_table[i] == cb) {
irq_callback_table[i] = NULL;
break;
}
}
if (i == DISP_MAX_IRQ_CALLBACK) {
DDPERR("%p which was not registered\n",
cb);
return -1;
}
return 0;
}
int disp_register_module_irq_callback(enum DISP_MODULE_ENUM module,
DDP_IRQ_CALLBACK cb)
{
int i;
if (module >= DISP_MODULE_NUM) {
DDPERR("Register IRQ with invalid module ID. module=%d\n",
module);
return -1;
}
if (cb == NULL) {
DDPERR("Register IRQ with invalid cb.\n");
return -1;
}
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_module_callback_table[module][i] == cb)
break;
}
if (i < DISP_MAX_IRQ_CALLBACK)
return 0;
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_module_callback_table[module][i] == NULL)
break;
}
if (i == DISP_MAX_IRQ_CALLBACK) {
DDPERR("No enough callback entries for module %d.\n", module);
return -1;
}
irq_module_callback_table[module][i] = cb;
return 0;
}
int disp_unregister_module_irq_callback(enum DISP_MODULE_ENUM module,
DDP_IRQ_CALLBACK cb)
{
int i;
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_module_callback_table[module][i] == cb) {
irq_module_callback_table[module][i] = NULL;
break;
}
}
if (i == DISP_MAX_IRQ_CALLBACK) {
DDPERR(
"Try to unregister callback function with was not registered. module=%d cb=%p\n",
module, cb);
return -1;
}
return 0;
}
void disp_invoke_irq_callbacks(enum DISP_MODULE_ENUM module, unsigned int param)
{
int i;
for (i = 0; i < DISP_MAX_IRQ_CALLBACK; i++) {
if (irq_callback_table[i])
irq_callback_table[i](module, param);
if (irq_module_callback_table[module][i])
irq_module_callback_table[module][i] (module, param);
}
}
/* TODO: move each irq to module driver */
unsigned int rdma_start_irq_cnt[2] = { 0, 0 };
unsigned int rdma_done_irq_cnt[2] = { 0, 0 };
unsigned int rdma_underflow_irq_cnt[2] = { 0, 0 };
unsigned int rdma_targetline_irq_cnt[2] = { 0, 0 };
irqreturn_t disp_irq_handler(int irq, void *dev_id)
{
enum DISP_MODULE_ENUM module = DISP_MODULE_UNKNOWN;
unsigned int reg_val = 0;
unsigned int index = 0;
unsigned int m_id = 0;
unsigned int reg_temp_val = 0;
if (irq == ddp_get_module_irq(DISP_MODULE_DSI0)) {
if (ddp_get_module_irq(DISP_MODULE_DSI0) == irq) {
index = 0;
module = DISP_MODULE_DSI0;
} else if (ddp_get_module_irq(DISP_MODULE_DSI1) == irq) {
index = 1;
module = DISP_MODULE_DSI1;
}
if (module == DISP_MODULE_DSI0)
reg_val =
(DISP_REG_GET(DISPSYS_DSI0_BASE + 0xC) & 0xffff);
else
reg_val =
(DISP_REG_GET(DISPSYS_DSI1_BASE + 0xC) & 0xffff);
DDPIRQ("%s irq_status = 0x%x\n",
ddp_get_module_name(module), reg_val);
reg_temp_val = reg_val;
/* rd_rdy don't clear and wait for ESD &
* Read LCM will clear the bit.
*/
if (disp_irq_esd_cust_get() == 1)
reg_temp_val = reg_val & 0xfffe;
if (module == DISP_MODULE_DSI0)
DISP_CPU_REG_SET(DISPSYS_DSI0_BASE + 0xC,
~reg_temp_val);
else
DISP_CPU_REG_SET(DISPSYS_DSI1_BASE + 0xC,
~reg_temp_val);
} else if (irq == ddp_get_module_irq(DISP_MODULE_OVL0) ||
irq == ddp_get_module_irq(DISP_MODULE_OVL0_2L)) {
/*
* not use this module
* irq == ddp_get_module_irq(DISP_MODULE_OVL1_2L)) {
*/
module = disp_irq_to_module(irq);
index = ovl_to_index(module);
reg_val = DISP_REG_GET(DISP_REG_OVL_INTSTA +
ovl_base_addr(module));
DDPIRQ("%s irq_status = 0x%x\n",
ddp_get_module_name(module), reg_val);
if (reg_val & (1 << 0))
DDPIRQ("IRQ: %s reg commit!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 1))
DDPIRQ("IRQ: %s frame done!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 2))
DDPERR("IRQ: %s frame underflow! cnt=%d\n",
ddp_get_module_name(module),
cnt_ovl_underflow[index]++);
if (reg_val & (1 << 3))
DDPIRQ("IRQ: %s sw reset done\n",
ddp_get_module_name(module));
if (reg_val & (1 << 4))
DDPERR("IRQ: %s hw reset done\n",
ddp_get_module_name(module));
if (reg_val & (1 << 5))
DDPERR("IRQ: %s-L0 not complete until EOF!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 6))
DDPERR("IRQ: %s-L1 not complete until EOF!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 7))
DDPERR("IRQ: %s-L2 not complete until EOF!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 8))
DDPERR("IRQ: %s-L3 not complete until EOF!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 13))
DDPERR("IRQ: %s abnormal SOF!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 14))
DDPIRQ("IRQ: %s frame start!\n",
ddp_get_module_name(module));
DISP_CPU_REG_SET(
DISP_REG_OVL_INTSTA + ovl_base_addr(module), ~reg_val);
mmprofile_log_ex(ddp_mmp_get_events()->OVL_IRQ[index],
MMPROFILE_FLAG_PULSE, reg_val, 0);
if (reg_val & 0x1e0)
mmprofile_log_ex(
ddp_mmp_get_events()->ddp_abnormal_irq,
MMPROFILE_FLAG_PULSE,
(index << 16) | reg_val, module);
} else if (irq == ddp_get_module_irq(DISP_MODULE_WDMA0)) {
index = 0;
module = DISP_MODULE_WDMA0;
reg_val = DISP_REG_GET(DISP_REG_WDMA_INTSTA);
DDPIRQ("%s irq_status = 0x%x\n",
ddp_get_module_name(module), reg_val);
if (reg_val & (1 << 0))
DDPIRQ("IRQ: WDMA%d frame done!\n", index);
if (reg_val & (1 << 1)) {
DDPERR("IRQ: WDMA%d underrun! cnt=%d\n", index,
cnt_wdma_underflow[index]++);
disp_irq_log_module |= 1UL << module;
}
/* clear intr */
DISP_CPU_REG_SET(DISP_REG_WDMA_INTSTA, ~reg_val);
mmprofile_log_ex(ddp_mmp_get_events()->WDMA_IRQ[index],
MMPROFILE_FLAG_PULSE, reg_val,
DISP_REG_GET(DISP_REG_WDMA_CLIP_SIZE));
if (reg_val & 0x2)
mmprofile_log_ex(ddp_mmp_get_events()->ddp_abnormal_irq,
MMPROFILE_FLAG_PULSE,
(cnt_wdma_underflow[index] << 24) |
(index << 16) | reg_val,
module);
} else if (irq == ddp_get_module_irq(DISP_MODULE_RDMA0)) {
if (ddp_get_module_irq(DISP_MODULE_RDMA0) == irq) {
index = 0;
module = DISP_MODULE_RDMA0;
}
reg_val = DISP_REG_GET(DISP_REG_RDMA_INT_STATUS +
index * DISP_RDMA_INDEX_OFFSET);
DDPIRQ("%s irq_status = 0x%x\n",
ddp_get_module_name(module), reg_val);
if (reg_val & (1 << 0))
DDPIRQ("IRQ: RDMA%d reg update done!\n", index);
if (reg_val & (1 << 2)) {
mmprofile_log_ex(
ddp_mmp_get_events()->SCREEN_UPDATE[index],
MMPROFILE_FLAG_END, reg_val,
DISP_REG_GET(DISPSYS_RDMA0_BASE + 0x4));
rdma_end_time[index] = sched_clock();
DDPIRQ("IRQ: RDMA%d frame done!\n", index);
rdma_done_irq_cnt[index]++;
}
if (reg_val & (1 << 1)) {
mmprofile_log_ex(
ddp_mmp_get_events()->SCREEN_UPDATE[index],
MMPROFILE_FLAG_START, reg_val,
DISP_REG_GET(DISPSYS_RDMA0_BASE + 0x4));
rdma_start_time[index] = sched_clock();
DDPIRQ("IRQ: RDMA%d frame start!\n", index);
rdma_start_irq_cnt[index]++;
}
if (reg_val & (1 << 3)) {
mmprofile_log_ex(
ddp_mmp_get_events()->SCREEN_UPDATE[index],
MMPROFILE_FLAG_PULSE, reg_val,
DISP_REG_GET(DISPSYS_RDMA0_BASE + 0x4));
DDPERR("IRQ: RDMA%d abnormal! cnt=%d\n",
index, cnt_rdma_abnormal[index]++);
disp_irq_log_module |= 1UL << module;
}
if (reg_val & (1 << 4)) {
mmprofile_log_ex(
ddp_mmp_get_events()->SCREEN_UPDATE[index],
MMPROFILE_FLAG_PULSE, reg_val, 1);
cnt_rdma_underflow[index]++;
DDPERR(
"IRQ: RDMA%d underflow! cnt=%d pix(%d,%d,%d,%d)\n",
index, cnt_rdma_underflow[index],
DISP_REG_GET(DISP_REG_RDMA_IN_P_CNT +
DISP_RDMA_INDEX_OFFSET * index),
DISP_REG_GET(DISP_REG_RDMA_IN_LINE_CNT +
DISP_RDMA_INDEX_OFFSET * index),
DISP_REG_GET(DISP_REG_RDMA_OUT_P_CNT +
DISP_RDMA_INDEX_OFFSET * index),
DISP_REG_GET(DISP_REG_RDMA_OUT_LINE_CNT +
DISP_RDMA_INDEX_OFFSET * index));
disp_irq_log_module |= 1UL << module;
rdma_underflow_irq_cnt[index]++;
disp_irq_rdma_underflow = 1;
}
if (reg_val & (1 << 5)) {
DDPIRQ("IRQ: RDMA%d target line!\n", index);
rdma_targetline_irq_cnt[index]++;
}
/* clear intr */
DISP_CPU_REG_SET(DISP_REG_RDMA_INT_STATUS +
index * DISP_RDMA_INDEX_OFFSET, ~reg_val);
mmprofile_log_ex(ddp_mmp_get_events()->RDMA_IRQ[index],
MMPROFILE_FLAG_PULSE, reg_val, 0);
if (reg_val & 0x18)
mmprofile_log_ex(ddp_mmp_get_events()->ddp_abnormal_irq,
MMPROFILE_FLAG_PULSE,
(rdma_underflow_irq_cnt[index] << 24) |
(index << 16) | reg_val, module);
} else if (irq == ddp_get_module_irq(DISP_MODULE_MUTEX)) {
/* mutex0: perimary disp */
/* mutex1: sub disp */
/* mutex2: aal */
unsigned int reg_val1 = 0;
module = DISP_MODULE_MUTEX;
reg_val = DISP_REG_GET(DISP_REG_CONFIG_MUTEX_INTSTA) &
DISP_MUTEX_INT_MSK;
reg_val1 = DISP_REG_GET(DISP_REG_CONFIG_MUTEX_INTSTA_1);
DDPIRQ("%s, irq_status = 0x%x, irq_status1 = 0x%x\n",
ddp_get_module_name(module), reg_val, reg_val1);
for (m_id = 0; m_id < DISP_MUTEX_DDP_COUNT; m_id++) {
if (reg_val & (0x1 << m_id)) {
DDPIRQ("IRQ: mutex%d sof!\n", m_id);
mmprofile_log_ex(
ddp_mmp_get_events()->MUTEX_IRQ[m_id],
MMPROFILE_FLAG_PULSE, reg_val, 0);
}
if (reg_val & (0x1 << (m_id + DISP_MUTEX_TOTAL))) {
DDPIRQ("IRQ: mutex%d eof!\n", m_id);
mmprofile_log_ex(
ddp_mmp_get_events()->MUTEX_IRQ[m_id],
MMPROFILE_FLAG_PULSE, reg_val, 1);
}
}
DISP_CPU_REG_SET(DISP_REG_CONFIG_MUTEX_INTSTA, ~reg_val);
DISP_CPU_REG_SET(DISP_REG_CONFIG_MUTEX_INTSTA_1, ~reg_val1);
} else if (irq == ddp_get_module_irq(DISP_MODULE_AAL0)) {
module = DISP_MODULE_AAL0;
reg_val = DISP_REG_GET(DISP_AAL_INTSTA);
disp_aal_on_end_of_frame();
} else if (irq == ddp_get_module_irq(DISP_MODULE_CCORR0)) {
module = DISP_MODULE_CCORR0;
reg_val = DISP_REG_GET(DISP_REG_CCORR_INTSTA);
disp_ccorr_on_end_of_frame();
} else if (irq == ddp_get_module_irq(DISP_MODULE_CONFIG)) {
/* MMSYS error intr */
reg_val = DISP_REG_GET(DISP_REG_CONFIG_MMSYS_INTSTA) & 0x7;
if (reg_val & (1 << 0))
DDPERR("MMSYS to MFG APB TX Error!\n");
if (reg_val & (1 << 1))
DDPERR("MMSYS to MJC APB TX Error!\n");
if (reg_val & (1 << 2))
DDPERR("PWM APB TX Error!\n");
DISP_CPU_REG_SET(DISP_REG_CONFIG_MMSYS_INTSTA, ~reg_val);
} else if (irq == ddp_get_module_irq(DISP_MODULE_POSTMASK0)) {
module = DISP_MODULE_POSTMASK0;
reg_val = DISP_REG_GET(DISP_REG_POSTMASK_INTSTA +
postmask_base_addr(module));
DDPIRQ("%s irq_status = 0x%x\n",
ddp_get_module_name(module), reg_val);
if (reg_val & (1 << 0))
DDPIRQ("IRQ: %s input frame end!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 1))
DDPIRQ("IRQ: %s output frame end!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 2))
DDPIRQ("IRQ: %s frame start!\n",
ddp_get_module_name(module));
if (reg_val & (1 << 4)) {
DDPERR("IRQ: %s abnormal SOF! cnt=%d\n",
ddp_get_module_name(module),
cnt_postmask_abnormal);
cnt_postmask_abnormal++;
}
if (reg_val & (1 << 8)) {
DDPERR("IRQ: %s frame underflow! cnt=%d\n",
ddp_get_module_name(module),
cnt_postmask_underflow);
cnt_postmask_underflow++;
}
DISP_CPU_REG_SET(DISP_REG_POSTMASK_INTSTA +
postmask_base_addr(module), ~reg_val);
mmprofile_log_ex(ddp_mmp_get_events()->POSTMASK_IRQ,
MMPROFILE_FLAG_PULSE, reg_val, 0);
if (reg_val & 0x110)
mmprofile_log_ex(ddp_mmp_get_events()->ddp_abnormal_irq,
MMPROFILE_FLAG_PULSE, reg_val, module);
} else {
module = DISP_MODULE_UNKNOWN;
reg_val = 0;
DDPERR("invalid irq=%d\n ", irq);
}
disp_invoke_irq_callbacks(module, reg_val);
if (disp_irq_log_module != 0)
wake_up_interruptible(&disp_irq_log_wq);
mmprofile_log_ex(ddp_mmp_get_events()->DDP_IRQ,
MMPROFILE_FLAG_PULSE, module, reg_val);
return IRQ_HANDLED;
}
static void disp_irq_rdma_underflow_aee_trigger(void)
{
static unsigned long long last_timer;
static unsigned int considerable_cnt;
if (disp_irq_rdma_underflow) {
/* Request highest dvfs */
#ifdef MTK_FB_MMDVFS_SUPPORT
primary_display_request_dvfs_perf(0,
HRT_LEVEL_LEVEL2);
#endif
if (disp_helper_get_option(DISP_OPT_RDMA_UNDERFLOW_AEE)) {
/* Just count underflow which happens more frequently */
if (last_timer != 0) {
unsigned long long freq = 1000 * 1000000;
do_div(freq, sched_clock() - last_timer);
if (freq > 0)
considerable_cnt++;
else
considerable_cnt = 0;
}
/* Should trigger AEE as */
/* more than 5 times continuous underflow happens */
/* TODO: need more precise data from test */
/*need trigger aee when RDMA underflow*/
/*trigger sspm to collect SMI, EMI debug info */
/*increase cnt to 20 to avoid too many underflow aee*/
if (considerable_cnt >= 20) {
primary_display_diagnose(__func__, __LINE__);
#if 0 /*SHANG: TODO: wait smi offer this API */
smi_dumpDebugMsg();
#endif
DDPAEE("RDMA0 underflow!cnt=%d,sys_tim=%u\n",
cnt_rdma_underflow[0],
(u32)arch_counter_get_cntvct());
considerable_cnt = 0;
}
last_timer = sched_clock();
}
disp_irq_rdma_underflow = 0;
}
}
static int disp_irq_log_kthread_func(void *data)
{
unsigned int i = 0;
while (1) {
wait_event_interruptible(disp_irq_log_wq, disp_irq_log_module);
DDPMSG("%s dump intr register: disp_irq_log_module=%ld\n",
__func__, disp_irq_log_module);
for (i = 0; i < DISP_MODULE_NUM; i++) {
if ((disp_irq_log_module & (1UL << i)) != 0)
ddp_dump_reg(i);
}
disp_irq_log_module = 0;
/* rdma underflow trigger aee */
disp_irq_rdma_underflow_aee_trigger();
}
return 0;
}
int disp_init_irq(void)
{
if (irq_init)
return 0;
irq_init = 1;
DDPMSG("disp_init_irq\n");
/* create irq log thread */
init_waitqueue_head(&disp_irq_log_wq);
disp_irq_log_task = kthread_create(disp_irq_log_kthread_func,
NULL, "ddp_irq_log_kthread");
if (IS_ERR(disp_irq_log_task))
DDPERR(" can not create disp_irq_log_task kthread\n");
/* wake_up_process(disp_irq_log_task); */
return 0;
}