blob: 8a9d9caab4819336e941c4cc7a76e7f107ca1554 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/pwm.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "common.h"
#ifdef DEVICE_NAME
#undef DEVICE_NAME
#define DEVICE_NAME "amhdmitx" /* name is same as before */
#endif
static void dump32(struct seq_file *s, u32 start, u32 end)
{
u32 value = 0;
for (; start <= end; start += 4) {
value = hd21_read_reg(start);
if (value)
seq_printf(s, "[0x%08x] 0x%08x\n",
TO21_PHY_ADDR(start), value);
}
}
static int dump_regs_show(struct seq_file *s, void *p)
{
seq_puts(s, "\n--------misc registers--------\n");
// ((0x0000 << 2) + 0xfe008000) ~ ((0x00f3 << 2) + 0xfe008000)
dump32(s, ANACTRL_SYS0PLL_CTRL0, ANACTRL_DIF_PHY_STS);
// ((0x0001 << 2) + 0xfe000000) ~ ((0x0128 << 2) + 0xfe000000)
dump32(s, CLKCTRL_OSCIN_CTRL, CLKCTRL_EFUSE_A73_CFG2);
// ((0x0000 << 2) + 0xfe00c000) ~ ((0x027f << 2) + 0xfe00c000)
dump32(s, PWRCTRL_PWR_ACK0, PWRCTRL_A73TOP_FSM_JUMP);
// ((0x1b00 << 2) + 0xff000000) ~ ((0x1bea << 2) + 0xff000000)
dump32(s, ENCI_VIDEO_MODE, ENCP_VRR_CTRL1);
// ((0x1c00 << 2) + 0xff000000) ~((0x1cfc << 2) + 0xff000000)
dump32(s, ENCI_DVI_HSO_BEGIN, VPU_VENCL_DITH_LUT_12);
// ((0x2158 << 2) + 0xff000000) ~ ((0x2250 << 2) + 0xff000000)
dump32(s, ENCP1_VFIFO2VD_CTL, ENCP1_VFIFO2VD_CTL2);
// ((0x2358 << 2) + 0xff000000) ~ ((0x2450 << 2) + 0xff000000)
dump32(s, ENCP2_VFIFO2VD_CTL, ENCP2_VFIFO2VD_CTL2);
// ((0x2451 << 2) + 0xff000000) ~ ((0x24fc << 2) + 0xff000000)
dump32(s, VENC2_DVI_SETTING_MORE, VPU2_VENCL_DITH_LUT_12);
// ((0x2701 << 2) + 0xff000000) ~ ((0x24fc << 2) + 0xff000000)
dump32(s, VPU_CRC_CTRL, VPUCTRL_REG_ADDR(0x27fd));
return 0;
}
static ssize_t dump_regs_write(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos)
{
/* TODO */
return count;
}
static int dump_regs_open(struct inode *inode, struct file *file)
{
return single_open(file, dump_regs_show, inode->i_private);
}
static const struct file_operations dump_busreg_fops = {
.open = dump_regs_open,
.read = seq_read,
.write = dump_regs_write,
.release = single_release,
};
static void dumptop(struct seq_file *s, u32 start, u32 end)
{
u32 value = 0;
start = (((start) & 0xffff) | TOP_OFFSET_MASK);
end = (((end) & 0xffff) | TOP_OFFSET_MASK);
for (; start <= end; start += 4) {
value = hdmitx21_rd_reg(start);
if (value)
seq_printf(s, "[0x%08x] 0x%02x\n", start, value);
}
}
static void dumpcor(struct seq_file *s, u32 start, u32 end)
{
u32 value = 0;
for (; start <= end; start++) {
value = hdmitx21_rd_reg(start);
if (value)
seq_printf(s, "[0x%08x] 0x%02x\n", start, value);
}
}
static int dump_hdmireg_show(struct seq_file *s, void *p)
{
seq_puts(s, "\n--------HDMITX registers--------\n");
// 0xfe300000 ~ 0xfe300000 + (0x041 << 2)
dumptop(s, HDMITX_TOP_SW_RESET, HDMITX_TOP_SECURE_DATA);
// 0x00000000 - 0x00000018
dumpcor(s, INTR3_IVCTX, AON_CYP_CTL_IVCTX);
// 0x00000100 - 0x00000130
dumpcor(s, VND_IDL_IVCTX, TOP_INTR_IVCTX);
// 0x00000200 - 0x000002d5
dumpcor(s, DEBUG_MODE_EN_IVCTX, DROP_GEN_TYPE_5_IVCTX);
// 0x00000300 - 0x0000031a
dumpcor(s, TX_ZONE_CTL0_IVCTX, FIFO_10TO20_CTRL_IVCTX);
// 0x00000400 - 0x000004a0
dumpcor(s, BIST_RST_IVCTX, CR_BLACK_HIGH_IVCTX);
// 0x00000607 - 0x000006ff
dumpcor(s, TPI_MISC_IVCTX, RSVD11_HW_TPI_IVCTX);
// 0x00000800 - 0x00000876
dumpcor(s, CP2TX_CTRL_0_IVCTX, CP2TX_GP1_IVCTX);
// 0x000008a0 - 0x000008d0
dumpcor(s, HDCP2X_DEBUG_CTRL0_IVCTX, HDCP2X_DEBUG_STAT16_IVCTX);
// 0x00000900 - 0x00000934
dumpcor(s, SCRCTL_IVCTX, RSVD1_HDMI2_IVCTX);
// 0x00000a00 - 0x00000a6e
dumpcor(s, RSVD0_AIP_IVCTX, AUDIO_CLK_DIV_IVCTX);
// 0x00000b00 - 0x00000be1
dumpcor(s, VP_FEATURES_IVCTX, VP_EMBD_SYNC_ENC_CONFIG_IVCTX);
// 0x00000d00 - 0x00000d3c
dumpcor(s, VP_CMS_CSC0_FEATURES_IVCTX,
VP_CMS_CSC0_MULTI_CSC_OUT_RCR_OFFSET_IVCTX);
//0x00000d80 - 0x00000ddc
dumpcor(s, VP_CMS_CSC1_FEATURES_IVCTX, VP_CMS_CSC1_PIXCAP_OUT_CR_IVCTX);
// 0x00000f00 - 0x00000f26
dumpcor(s, D_HDR_GEN_CTL_IVCTX, D_HDR_SPARE_9_IVCTX);
// 0x00000f80 - 0x00000fa9
dumpcor(s, DSC_PKT_GEN_CTL_IVCTX, DSC_PKT_SPARE_9_IVCTX);
dump_infoframe_packets(s);
return 0;
}
static int dump_hdmireg_open(struct inode *inode, struct file *file)
{
return single_open(file, dump_hdmireg_show, inode->i_private);
}
static const struct file_operations dump_hdmireg_fops = {
.open = dump_hdmireg_open,
.read = seq_read,
.release = single_release,
};
#define CONNECT2REG(reg) ({ \
typeof(reg) _reg = (reg); \
(hdmitx21_rd_reg(_reg) & 0xff) + \
((hdmitx21_rd_reg(_reg + 1) & 0xff) << 8); })
#define CONNECT3REG(reg) ({ \
typeof(reg) _reg = (reg); \
(hdmitx21_rd_reg(_reg) & 0xff) + \
((hdmitx21_rd_reg(_reg + 1) & 0xff) << 8) + \
((hdmitx21_rd_reg(_reg + 2) & 0xff) << 16); })
#define CONNECT4REG(reg) ({ \
typeof(reg) _reg = (reg); \
(hdmitx21_rd_reg(_reg) & 0xff) + \
((hdmitx21_rd_reg(_reg + 1) & 0xff) << 8) + \
((hdmitx21_rd_reg(_reg + 2) & 0xff) << 16) + \
((hdmitx21_rd_reg(_reg + 3) & 0xff) << 24); })
static int dump_hdmivpfdet_show(struct seq_file *s, void *p)
{
u32 reg;
u32 val;
u32 total, active, front, sync, back, blank;
seq_puts(s, "\n--------vp fdet info--------\n");
hdmitx21_wr_reg(VP_FDET_CLEAR_IVCTX, 0);
hdmitx21_wr_reg(VP_FDET_STATUS_IVCTX, 0);
mdelay(50); /* at least 1 frame? */
reg = VP_FDET_FRAME_RATE_IVCTX;
val = CONNECT3REG(reg);
if (val)
seq_printf(s, "frame_rate [%x] 0x%x 200000000/%d Hz\n",
reg, val, val);
reg = VP_FDET_PIXEL_COUNT_IVCTX;
val = CONNECT2REG(reg);
active = val;
if (val)
seq_printf(s, "hactive [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_HFRONT_COUNT_IVCTX;
val = CONNECT2REG(reg);
front = val;
if (val)
seq_printf(s, "hfront [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_HSYNC_HIGH_COUNT_IVCTX;
val = CONNECT2REG(reg);
sync = val;
if (val)
seq_printf(s, "hsync [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_HBACK_COUNT_IVCTX;
val = CONNECT2REG(reg);
back = val;
if (val)
seq_printf(s, "hback [%x] 0x%x %d\n", reg, val, val);
blank = front + sync + back;
total = active + blank;
if (blank)
seq_printf(s, "Calc hblank 0x%x %d\n", blank, blank);
if (total)
seq_printf(s, "Calc htotal 0x%x %d\n", total, total);
reg = VP_FDET__LINE__COUNT_IVCTX;
val = CONNECT2REG(reg);
active = val;
if (val)
seq_printf(s, "vactive [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_VSYNC_HIGH_COUNT_EVEN_IVCTX;
val = CONNECT2REG(reg);
sync = val;
if (val)
seq_printf(s, "vsync_high_count_even [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_VFRONT_COUNT_EVEN_IVCTX;
val = CONNECT2REG(reg);
front = val;
if (val)
seq_printf(s, "vfront_count_even [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_VBACK_COUNT_EVEN_IVCTX;
val = CONNECT2REG(reg);
back = val;
if (val)
seq_printf(s, "vback_count_even [%x] 0x%x %d\n",
reg, val, val);
blank = front + sync + back;
total = active + blank;
if (blank)
seq_printf(s, "Calc vblank 0x%x %d\n", blank, blank);
if (total)
seq_printf(s, "Calc vtotal 0x%x %d\n", total, total);
reg = VP_FDET_CONFIG_IVCTX;
val = hdmitx21_rd_reg(reg);
if (val)
seq_printf(s, "[%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_STATUS_IVCTX;
val = hdmitx21_rd_reg(reg);
if (val) {
seq_printf(s, "status [%x] 0x%x %d\n", reg, val, val);
seq_printf(s, " hsync_polarity %d\n", (val >> 0) & 1);
seq_printf(s, " vsync_polarity %d\n", (val >> 1) & 1);
seq_printf(s, " interlaced %d\n", (val >> 2) & 1);
seq_printf(s, " video656 %d\n", (val >> 3) & 1);
}
reg = VP_FDET_INTERLACE_THRESHOLD_IVCTX;
val = hdmitx21_rd_reg(reg);
if (val)
seq_printf(s, "interlace_threshold [%x] %x %d\n",
reg, val, val);
reg = VP_FDET_FRAME_RATE_DELTA_THRESHOLD_IVCTX;
val = CONNECT3REG(reg);
if (val)
seq_printf(s, "frame_rate_delta_threshold [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_HSYNC_LOW_COUNT_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "hsync_low_count [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_VSYNC_LOW_COUNT_EVEN_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "vsync_low_count_even [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_VSYNC_LOW_COUNT_ODD_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "vsync_low_count_odd [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_VSYNC_HIGH_COUNT_ODD_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "vsync_high_count_odd [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_VFRONT_COUNT_ODD_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "vfront_count_odd [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_VBACK_COUNT_ODD_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "frame_count [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_FRAME_COUNT_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "frame_count [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET__LINE__RATE_DELTA_THRESHOLD_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "line_rate_delta_threshold [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET__LINE__RATE_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "line_rate [%x] 0x%x %d\n", reg, val, val);
reg = VP_FDET_VSYNC_HSYNC_OFFSET_EVEN_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "vsync_hsync_offset_even [%x] 0x%x %d\n",
reg, val, val);
reg = VP_FDET_VSYNC_HSYNC_OFFSET_ODD_IVCTX;
val = CONNECT2REG(reg);
if (val)
seq_printf(s, "vsync_hsync_offset_odd [%x] 0x%x %d\n",
reg, val, val);
#define PR_DETAIL(n, str) \
do { \
if ((val >> (n)) & 1) \
seq_printf(s, " %s", str) ; \
} while (0)
reg = VP_FDET_IRQ_MASK_IVCTX;
val = CONNECT3REG(reg);
if (val) {
seq_printf(s, "irq_mask [%x] 0x%x %d\n", reg, val, val);
PR_DETAIL(0, "hsync_polarity");
PR_DETAIL(1, "vsync_polarity");
PR_DETAIL(2, "interlaced");
PR_DETAIL(3, "video656");
PR_DETAIL(4, "frame_rate");
PR_DETAIL(5, "pixel_count");
PR_DETAIL(6, "line_count");
PR_DETAIL(7, "hsync_low_count");
PR_DETAIL(8, "hsync_high_count");
PR_DETAIL(9, "hfront_count");
PR_DETAIL(10, "hback_count");
PR_DETAIL(11, "vsync_low_count_even");
PR_DETAIL(12, "vsync_high_count_even");
PR_DETAIL(13, "vfront_count_even");
PR_DETAIL(14, "vback_count_even");
PR_DETAIL(15, "vsync_low_count_odd");
PR_DETAIL(16, "vsync_high_count_odd");
PR_DETAIL(17, "vfront_count_odd");
PR_DETAIL(18, "vback_count_odd");
PR_DETAIL(19, "line_rate");
PR_DETAIL(20, "vsync_hsync_offset_even");
PR_DETAIL(21, "vsync_hsync_offset_odd");
PR_DETAIL(22, "frame_and_pixel_cnt_done");
seq_puts(s, "\n");
}
reg = VP_FDET_IRQ_STATUS_IVCTX;
val = CONNECT3REG(reg);
if (val) {
seq_printf(s, "irq_status [%x] 0x%x %d\n",
reg, val, val);
PR_DETAIL(0, "hsync_polarity");
PR_DETAIL(1, "vsync_polarity");
PR_DETAIL(2, "interlaced");
PR_DETAIL(3, "video656");
PR_DETAIL(4, "frame_rate");
PR_DETAIL(5, "pixel_count");
PR_DETAIL(6, "line_count");
PR_DETAIL(7, "hsync_low_count");
PR_DETAIL(8, "hsync_high_count");
PR_DETAIL(9, "hfront_count");
PR_DETAIL(10, "hback_count");
PR_DETAIL(11, "vsync_low_count_even");
PR_DETAIL(12, "vsync_high_count_even");
PR_DETAIL(13, "vfront_count_even");
PR_DETAIL(14, "vback_count_even");
PR_DETAIL(15, "vsync_low_count_odd");
PR_DETAIL(16, "vsync_high_count_odd");
PR_DETAIL(17, "vfront_count_odd");
PR_DETAIL(18, "vback_count_odd");
PR_DETAIL(19, "line_rate");
PR_DETAIL(20, "vsync_hsync_offset_even");
PR_DETAIL(21, "vsync_hsync_offset_odd");
PR_DETAIL(22, "frame_and_pixel_cnt_done");
seq_puts(s, "\n");
}
return 0;
}
static int dump_hdmivpfdet_open(struct inode *inode, struct file *file)
{
return single_open(file, dump_hdmivpfdet_show, inode->i_private);
}
static const struct file_operations dump_hdmivpfdet_fops = {
.open = dump_hdmivpfdet_open,
.read = seq_read,
.release = single_release,
};
static void hdmitx_parsing_avipkt(struct seq_file *s)
{
}
static void hdmitx_parsing_spdpkt(struct seq_file *s)
{
}
static void hdmitx_parsing_audpkt(struct seq_file *s)
{
}
static void hdmitx_parsing_gcppkt(struct seq_file *s)
{
}
static void hdmitx_parsing_acrpkt(struct seq_file *s)
{
}
static void hdmitx_parsing_audsamp(struct seq_file *s)
{
}
static void hdmitx_parsing_audchannelst(struct seq_file *s)
{
}
static void hdmitx_parsing_hdrpkt(struct seq_file *s)
{
}
static void hdmitx_parsing_vsifpkt(struct seq_file *s)
{
}
static int dump_hdmipkt_show(struct seq_file *s, void *p)
{
seq_puts(s, "\n--------HDMITX packets--------\n");
hdmitx_parsing_acrpkt(s);
hdmitx_parsing_audsamp(s);
hdmitx_parsing_gcppkt(s);
hdmitx_parsing_vsifpkt(s);
hdmitx_parsing_avipkt(s);
hdmitx_parsing_spdpkt(s);
hdmitx_parsing_audpkt(s);
hdmitx_parsing_audchannelst(s);
hdmitx_parsing_hdrpkt(s);
return 0;
}
static int dump_hdmipkt_open(struct inode *inode, struct file *file)
{
return single_open(file, dump_hdmipkt_show, inode->i_private);
}
static const struct file_operations dump_hdmipkt_fops = {
.open = dump_hdmipkt_open,
.read = seq_read,
.release = single_release,
};
static inline unsigned int get_msr_cts(void)
{
unsigned int ret = 0;
/* TODO */
return ret;
}
#define AUD_CTS_LOG_NUM 1000
static unsigned int cts_buf[AUD_CTS_LOG_NUM];
static int dump_audcts_show(struct seq_file *s, void *p)
{
int i;
unsigned int min = 0, max = 0, total = 0;
seq_puts(s, "\n--------HDMITX audio cts--------\n");
memset(cts_buf, 0, sizeof(cts_buf));
for (i = 0; i < AUD_CTS_LOG_NUM; i++) {
cts_buf[i] = get_msr_cts();
mdelay(1);
}
seq_puts(s, "cts change:\n");
for (i = 1; i < AUD_CTS_LOG_NUM; i++) {
if (cts_buf[i] > cts_buf[i - 1])
seq_printf(s, "dis: +%d [%d] %d [%d] %d\n",
cts_buf[i] - cts_buf[i - 1], i,
cts_buf[i], i - 1, cts_buf[i - 1]);
if (cts_buf[i] < cts_buf[i - 1])
seq_printf(s, "dis: %d [%d] %d [%d] %d\n",
cts_buf[i] - cts_buf[i - 1], i,
cts_buf[i], i - 1, cts_buf[i - 1]);
}
for (i = 0, min = max = cts_buf[0]; i < AUD_CTS_LOG_NUM; i++) {
total += cts_buf[i];
if (min > cts_buf[i])
min = cts_buf[i];
if (max < cts_buf[i])
max = cts_buf[i];
}
seq_printf(s, "\nCTS Min: %d Max: %d Avg: %d/1000\n\n",
min, max, total);
return 0;
}
static int dump_audcts_open(struct inode *inode, struct file *file)
{
return single_open(file, dump_audcts_show, inode->i_private);
}
static const struct file_operations dump_audcts_fops = {
.open = dump_audcts_open,
.read = seq_read,
.release = single_release,
};
struct hdmitx_dbg_files_s {
const char *name;
const umode_t mode;
const struct file_operations *fops;
};
static struct hdmitx_dbg_files_s hdmitx_dbg_files[] = {
{"bus_reg", S_IFREG | 0444, &dump_busreg_fops},
{"hdmi_reg", S_IFREG | 0444, &dump_hdmireg_fops},
{"hdmi_vpfdet", S_IFREG | 0444, &dump_hdmivpfdet_fops},
{"hdmi_pkt", S_IFREG | 0444, &dump_hdmipkt_fops},
{"aud_cts", S_IFREG | 0444, &dump_audcts_fops},
};
static struct dentry *hdmitx_dbgfs;
void hdmitx21_debugfs_init(void)
{
struct dentry *entry;
int i;
if (hdmitx_dbgfs)
return;
hdmitx_dbgfs = debugfs_create_dir(DEVICE_NAME, NULL);
if (!hdmitx_dbgfs) {
pr_err("can't create %s debugfs dir\n", DEVICE_NAME);
return;
}
for (i = 0; i < ARRAY_SIZE(hdmitx_dbg_files); i++) {
entry = debugfs_create_file(hdmitx_dbg_files[i].name,
hdmitx_dbg_files[i].mode,
hdmitx_dbgfs, NULL,
hdmitx_dbg_files[i].fops);
if (!entry)
pr_err("debugfs create file %s failed\n",
hdmitx_dbg_files[i].name);
}
}