| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Rockchip ISP1 Driver - Base driver |
| * |
| * Copyright (C) 2019 Collabora, Ltd. |
| * |
| * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd. |
| * Copyright (C) 2017 Rockchip Electronics Co., Ltd. |
| */ |
| |
| #include <linux/debugfs.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/minmax.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/seq_file.h> |
| #include <linux/string.h> |
| |
| #include "rkisp1-common.h" |
| #include "rkisp1-regs.h" |
| |
| struct rkisp1_debug_register { |
| u32 reg; |
| u32 shd; |
| const char * const name; |
| }; |
| |
| #define RKISP1_DEBUG_REG(name) { RKISP1_CIF_##name, 0, #name } |
| #define RKISP1_DEBUG_SHD_REG(name) { \ |
| RKISP1_CIF_##name, RKISP1_CIF_##name##_SHD, #name \ |
| } |
| |
| /* Keep this up-to-date when adding new registers. */ |
| #define RKISP1_MAX_REG_LENGTH 21 |
| |
| static int rkisp1_debug_dump_regs(struct rkisp1_device *rkisp1, |
| struct seq_file *m, unsigned int offset, |
| const struct rkisp1_debug_register *regs) |
| { |
| const int width = RKISP1_MAX_REG_LENGTH; |
| u32 val, shd; |
| int ret; |
| |
| ret = pm_runtime_get_if_in_use(rkisp1->dev); |
| if (ret <= 0) |
| return ret ? : -ENODATA; |
| |
| for (; regs->name; ++regs) { |
| val = rkisp1_read(rkisp1, offset + regs->reg); |
| |
| if (regs->shd) { |
| shd = rkisp1_read(rkisp1, offset + regs->shd); |
| seq_printf(m, "%*s: 0x%08x/0x%08x\n", width, regs->name, |
| val, shd); |
| } else { |
| seq_printf(m, "%*s: 0x%08x\n", width, regs->name, val); |
| } |
| } |
| |
| pm_runtime_put(rkisp1->dev); |
| |
| return 0; |
| } |
| |
| static int rkisp1_debug_dump_core_regs_show(struct seq_file *m, void *p) |
| { |
| static const struct rkisp1_debug_register registers[] = { |
| RKISP1_DEBUG_REG(VI_CCL), |
| RKISP1_DEBUG_REG(VI_ICCL), |
| RKISP1_DEBUG_REG(VI_IRCL), |
| RKISP1_DEBUG_REG(VI_DPCL), |
| RKISP1_DEBUG_REG(MI_CTRL), |
| RKISP1_DEBUG_REG(MI_BYTE_CNT), |
| RKISP1_DEBUG_REG(MI_CTRL_SHD), |
| RKISP1_DEBUG_REG(MI_RIS), |
| RKISP1_DEBUG_REG(MI_STATUS), |
| RKISP1_DEBUG_REG(MI_DMA_CTRL), |
| RKISP1_DEBUG_REG(MI_DMA_STATUS), |
| { /* Sentinel */ }, |
| }; |
| struct rkisp1_device *rkisp1 = m->private; |
| |
| return rkisp1_debug_dump_regs(rkisp1, m, 0, registers); |
| } |
| DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_core_regs); |
| |
| static int rkisp1_debug_dump_isp_regs_show(struct seq_file *m, void *p) |
| { |
| static const struct rkisp1_debug_register registers[] = { |
| RKISP1_DEBUG_REG(ISP_CTRL), |
| RKISP1_DEBUG_REG(ISP_ACQ_PROP), |
| RKISP1_DEBUG_REG(ISP_FLAGS_SHD), |
| RKISP1_DEBUG_REG(ISP_RIS), |
| RKISP1_DEBUG_REG(ISP_ERR), |
| RKISP1_DEBUG_SHD_REG(ISP_IS_H_OFFS), |
| RKISP1_DEBUG_SHD_REG(ISP_IS_V_OFFS), |
| RKISP1_DEBUG_SHD_REG(ISP_IS_H_SIZE), |
| RKISP1_DEBUG_SHD_REG(ISP_IS_V_SIZE), |
| { /* Sentinel */ }, |
| }; |
| struct rkisp1_device *rkisp1 = m->private; |
| |
| return rkisp1_debug_dump_regs(rkisp1, m, 0, registers); |
| } |
| DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_isp_regs); |
| |
| static int rkisp1_debug_dump_rsz_regs_show(struct seq_file *m, void *p) |
| { |
| static const struct rkisp1_debug_register registers[] = { |
| RKISP1_DEBUG_SHD_REG(RSZ_CTRL), |
| RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HY), |
| RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCB), |
| RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCR), |
| RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VY), |
| RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VC), |
| RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HY), |
| RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HC), |
| RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VY), |
| RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VC), |
| { /* Sentinel */ }, |
| }; |
| struct rkisp1_resizer *rsz = m->private; |
| |
| return rkisp1_debug_dump_regs(rsz->rkisp1, m, rsz->regs_base, registers); |
| } |
| DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_rsz_regs); |
| |
| static int rkisp1_debug_dump_mi_mp_show(struct seq_file *m, void *p) |
| { |
| static const struct rkisp1_debug_register registers[] = { |
| RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT), |
| RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT2), |
| RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_SHD), |
| RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT), |
| RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT), |
| RKISP1_DEBUG_REG(MI_MP_Y_SIZE_SHD), |
| RKISP1_DEBUG_REG(MI_MP_Y_OFFS_CNT_SHD), |
| { /* Sentinel */ }, |
| }; |
| struct rkisp1_device *rkisp1 = m->private; |
| |
| return rkisp1_debug_dump_regs(rkisp1, m, 0, registers); |
| } |
| DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_mi_mp); |
| |
| #define RKISP1_DEBUG_DATA_COUNT_BINS 32 |
| #define RKISP1_DEBUG_DATA_COUNT_STEP (4096 / RKISP1_DEBUG_DATA_COUNT_BINS) |
| |
| static int rkisp1_debug_input_status_show(struct seq_file *m, void *p) |
| { |
| struct rkisp1_device *rkisp1 = m->private; |
| u16 data_count[RKISP1_DEBUG_DATA_COUNT_BINS] = { }; |
| unsigned int hsync_count = 0; |
| unsigned int vsync_count = 0; |
| unsigned int i; |
| u32 data; |
| u32 val; |
| int ret; |
| |
| ret = pm_runtime_get_if_in_use(rkisp1->dev); |
| if (ret <= 0) |
| return ret ? : -ENODATA; |
| |
| /* Sample the ISP input port status 10000 times with a 1µs interval. */ |
| for (i = 0; i < 10000; ++i) { |
| val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_FLAGS_SHD); |
| |
| data = (val & RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_MASK) |
| >> RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_SHIFT; |
| data_count[data / RKISP1_DEBUG_DATA_COUNT_STEP]++; |
| |
| if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_HSYNC) |
| hsync_count++; |
| if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_VSYNC) |
| vsync_count++; |
| |
| udelay(1); |
| } |
| |
| pm_runtime_put(rkisp1->dev); |
| |
| seq_printf(m, "vsync: %u, hsync: %u\n", vsync_count, hsync_count); |
| seq_puts(m, "data:\n"); |
| for (i = 0; i < ARRAY_SIZE(data_count); ++i) |
| seq_printf(m, "- [%04u:%04u]: %u\n", |
| i * RKISP1_DEBUG_DATA_COUNT_STEP, |
| (i + 1) * RKISP1_DEBUG_DATA_COUNT_STEP - 1, |
| data_count[i]); |
| |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_input_status); |
| |
| void rkisp1_debug_init(struct rkisp1_device *rkisp1) |
| { |
| struct rkisp1_debug *debug = &rkisp1->debug; |
| struct dentry *regs_dir; |
| |
| debug->debugfs_dir = debugfs_create_dir(dev_name(rkisp1->dev), NULL); |
| |
| debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, |
| &debug->data_loss); |
| debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, |
| &debug->outform_size_error); |
| debugfs_create_ulong("img_stabilization_size_error", 0444, |
| debug->debugfs_dir, |
| &debug->img_stabilization_size_error); |
| debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir, |
| &debug->inform_size_error); |
| debugfs_create_ulong("irq_delay", 0444, debug->debugfs_dir, |
| &debug->irq_delay); |
| debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir, |
| &debug->mipi_error); |
| debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir, |
| &debug->stats_error); |
| debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir, |
| &debug->stop_timeout[RKISP1_MAINPATH]); |
| debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir, |
| &debug->stop_timeout[RKISP1_SELFPATH]); |
| debugfs_create_ulong("mp_frame_drop", 0444, debug->debugfs_dir, |
| &debug->frame_drop[RKISP1_MAINPATH]); |
| debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir, |
| &debug->frame_drop[RKISP1_SELFPATH]); |
| debugfs_create_ulong("complete_frames", 0444, debug->debugfs_dir, |
| &debug->complete_frames); |
| debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp1, |
| &rkisp1_debug_input_status_fops); |
| |
| regs_dir = debugfs_create_dir("regs", debug->debugfs_dir); |
| |
| debugfs_create_file("core", 0444, regs_dir, rkisp1, |
| &rkisp1_debug_dump_core_regs_fops); |
| debugfs_create_file("isp", 0444, regs_dir, rkisp1, |
| &rkisp1_debug_dump_isp_regs_fops); |
| debugfs_create_file("mrsz", 0444, regs_dir, |
| &rkisp1->resizer_devs[RKISP1_MAINPATH], |
| &rkisp1_debug_dump_rsz_regs_fops); |
| debugfs_create_file("srsz", 0444, regs_dir, |
| &rkisp1->resizer_devs[RKISP1_SELFPATH], |
| &rkisp1_debug_dump_rsz_regs_fops); |
| |
| debugfs_create_file("mi_mp", 0444, regs_dir, rkisp1, |
| &rkisp1_debug_dump_mi_mp_fops); |
| } |
| |
| void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1) |
| { |
| debugfs_remove_recursive(rkisp1->debug.debugfs_dir); |
| } |