Files
linux-nv-oot/drivers/misc/mods/mods_debugfs.c
Lael Jones 2ce330f11a misc: mods: Replace CONFIG_ARCH_TEGRA
The perforce version of the MODS kernel driver
does not pull in any of the Tegra specific driver
functions. This prevents the perforce driver
from being compiled on any Tegra system or non
Tegra system with CONFIG_ARCH_TEGRA=y.

To allow the perforce driver to be compiled
replace CONFIG_ARCH_TEGRA with MODS_HAS_TEGRA
which is set based on CONFIG_ARCH_TEGRA in git
but left unset in perforce.

Bug 3397113

Signed-off-by: Lael Jones <lajones@nvidia.com>
Change-Id: Ie113d632c4dcc372058b9a1e3a549a70b8f7c03f
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2607859
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com>
Reviewed-by: Chris Dragan <kdragan@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
GVS: Gerrit_Virtual_Submit
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2023-04-03 13:27:52 +00:00

767 lines
18 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* mods_debugfs.c - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA MODS kernel driver is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* NVIDIA MODS kernel driver 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.
*
* You should have received a copy of the GNU General Public License
* along with NVIDIA MODS kernel driver.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "mods_internal.h"
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/uaccess.h>
static struct dentry *mods_debugfs_dir;
#ifdef CONFIG_ARCH_TEGRA_19x_SOC
#include "mods_ras.h"
#endif
#if defined(MODS_HAS_TEGRA) && defined(CONFIG_TEGRA_KFUSE)
#include <soc/tegra/kfuse.h>
#endif
#ifdef CONFIG_TEGRA_DC
#include <video/tegra_dc_ext_kernel.h>
#include <../drivers/video/tegra/dc/dc_config.h>
#include <../drivers/video/tegra/dc/dsi.h>
static int mods_dc_color_formats_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 i, j;
for (i = 0; i < tegra_dc_get_numof_dispwindows(); i++) {
struct tegra_dc_win *win;
u32 *fmt_masks;
win = tegra_dc_get_window(dc, i);
if (!win)
continue;
fmt_masks = tegra_dc_parse_feature(dc, i, GET_WIN_FORMATS);
if (!fmt_masks)
continue;
seq_printf(s, "window_%u:", i);
for (j = 0; j < WIN_FEATURE_ENTRY_SIZE; j++)
seq_printf(s, " 0x%08x", fmt_masks[j]);
seq_puts(s, "\n");
}
return 0;
}
static int mods_dc_color_formats_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_color_formats_show, inode->i_private);
}
static const struct file_operations mods_dc_color_formats_fops = {
.open = mods_dc_color_formats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dc_blend_gen_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 i;
for (i = 0; i < tegra_dc_get_numof_dispwindows(); i++) {
struct tegra_dc_win *win;
u32 *blend_gen;
win = tegra_dc_get_window(dc, i);
if (!win)
continue;
blend_gen = tegra_dc_parse_feature(dc, i, HAS_GEN2_BLEND);
if (!blend_gen)
continue;
seq_printf(s, "window_%u: %u\n", i,
blend_gen[BLEND_GENERATION]);
}
return 0;
}
static int mods_dc_blend_gen_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_blend_gen_show, inode->i_private);
}
static const struct file_operations mods_dc_blend_gen_fops = {
.open = mods_dc_blend_gen_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dc_layout_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 i;
seq_puts(s, " Pitch Tiled Block\n");
for (i = 0; i < tegra_dc_get_numof_dispwindows(); i++) {
struct tegra_dc_win *win;
u32 *layouts;
win = tegra_dc_get_window(dc, i);
if (!win)
continue;
layouts = tegra_dc_parse_feature(dc, i, HAS_TILED);
if (!layouts)
continue;
seq_printf(s, "window_%u: %5u %5u %5u\n", i,
layouts[PITCHED_LAYOUT],
layouts[TILED_LAYOUT],
layouts[BLOCK_LINEAR]);
}
return 0;
}
static int mods_dc_layout_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_layout_show, inode->i_private);
}
static const struct file_operations mods_dc_layout_fops = {
.open = mods_dc_layout_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dc_invert_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 i;
seq_puts(s, " FlipH FlipV ScanColumn\n");
for (i = 0; i < tegra_dc_get_numof_dispwindows(); i++) {
struct tegra_dc_win *win;
u32 *invert_data;
win = tegra_dc_get_window(dc, i);
if (!win)
continue;
invert_data = tegra_dc_parse_feature(dc, i, GET_INVERT);
if (!invert_data)
continue;
seq_printf(s, "window_%u: %5u %5u %10u\n", i,
invert_data[H_INVERT],
invert_data[V_INVERT],
invert_data[SCAN_COLUMN]);
}
return 0;
}
static int mods_dc_invert_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_invert_show, inode->i_private);
}
static const struct file_operations mods_dc_invert_fops = {
.open = mods_dc_invert_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dc_interlaced_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 i;
const unsigned int head_interlaced = 1;
seq_printf(s, "head: %u\n", head_interlaced);
for (i = 0; i < tegra_dc_get_numof_dispwindows(); i++) {
struct tegra_dc_win *win;
u32 *interlaced;
win = tegra_dc_get_window(dc, i);
if (!win)
continue;
interlaced = tegra_dc_parse_feature(dc, i, HAS_INTERLACE);
if (!interlaced)
continue;
seq_printf(s, "window_%u: %u\n", i, interlaced[0]);
}
return 0;
}
static int mods_dc_interlaced_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_interlaced_show, inode->i_private);
}
static const struct file_operations mods_dc_interlaced_fops = {
.open = mods_dc_interlaced_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dc_window_mask_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
seq_printf(s, "0x%02lx\n", dc->valid_windows);
return 0;
}
static int mods_dc_window_mask_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_window_mask_show, inode->i_private);
}
static const struct file_operations mods_dc_window_mask_fops = {
.open = mods_dc_window_mask_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dc_scaling_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 i;
seq_puts(s, " UpH UpV DownH DownV\n");
for (i = 0; i < tegra_dc_get_numof_dispwindows(); i++) {
struct tegra_dc_win *win;
u32 *scaling;
win = tegra_dc_get_window(dc, i);
if (!win)
continue;
scaling = tegra_dc_parse_feature(dc, i, HAS_SCALE);
if (!scaling)
continue;
seq_printf(s, "window_%u: %3u %3u %5u %5u\n", i,
scaling[H_SCALE_UP],
scaling[V_SCALE_UP],
scaling[H_FILTER_DOWN],
scaling[V_FILTER_DOWN]);
}
return 0;
}
static int mods_dc_scaling_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_scaling_show, inode->i_private);
}
static const struct file_operations mods_dc_scaling_fops = {
.open = mods_dc_scaling_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mods_dsi_ganged_get(void *data, u64 *val)
{
struct tegra_dc_dsi_data *dsi = data;
*val = (u64)dsi->info.ganged_type;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_dsi_ganged_fops, mods_dsi_ganged_get, NULL,
"%llu\n");
static int mods_dsi_inst_get(void *data, u64 *val)
{
struct tegra_dc_dsi_data *dsi = data;
*val = (u64)dsi->info.dsi_instance;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_dsi_inst_fops, mods_dsi_inst_get, NULL, "%llu\n");
static int mods_dc_border_get(void *data, u64 *val)
{
struct tegra_dc *dc = data;
u32 blender_reg = DC_DISP_BLEND_BACKGROUND_COLOR;
if (!dc->enabled)
*val = 0ULL;
else
*val = (u64)tegra_dc_readl_exported(dc, blender_reg);
return 0;
}
static int mods_dc_border_set(void *data, u64 val)
{
struct tegra_dc *dc = data;
u32 blender_reg = DC_DISP_BLEND_BACKGROUND_COLOR;
if (!dc->enabled)
return 0;
mutex_lock(&dc->lock);
tegra_dc_get(dc);
tegra_dc_writel_exported(dc, val, blender_reg);
tegra_dc_writel_exported(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
tegra_dc_put(dc);
mutex_unlock(&dc->lock);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_dc_border_fops, mods_dc_border_get,
mods_dc_border_set, "0x%llx\n");
static int mods_dc_ocp_show(struct seq_file *s, void *unused)
{
seq_puts(s, "rgb\n");
return 0;
}
static int mods_dc_ocp_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_ocp_show, inode->i_private);
}
static const struct file_operations mods_dc_ocp_fops = {
.open = mods_dc_ocp_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#define DC_DISP_CSC2_CONTROL 0x4ef
#define CSC2_OUTPUT_COLOR_RGB (0 << 0)
#define CSC2_OUTPUT_COLOR_709 (1 << 0)
#define CSC2_OUTPUT_COLOR_601 (2 << 0)
#define CSC2_OUTPUT_COLOR(val) (val & 0x3)
#define CSC2_LIMIT_RGB_DISABLE (0 << 2)
#define CSC2_LIMIT_RGB_ENABLE (1 << 2)
static int mods_dc_oc_show(struct seq_file *s, void *unused)
{
seq_puts(s, "rgb\n");
return 0;
}
static int mods_dc_oc_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_oc_show, inode->i_private);
}
static ssize_t mods_dc_oc_write(struct file *file, const char __user *user_buf,
size_t size, loff_t *ppos)
{
char buf[8];
int buf_size;
buf_size = min(size, (sizeof(buf) - 1));
if (strncpy_from_user(buf, user_buf, buf_size) < 0)
return -EFAULT;
buf[buf_size] = 0;
if (strncmp(buf, "rgb", 3) != 0)
return -EINVAL;
*ppos += size;
return size;
}
static const struct file_operations mods_dc_oc_fops = {
.open = mods_dc_oc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = mods_dc_oc_write,
};
static int mods_dc_ddc_bus_get(void *data, u64 *val)
{
struct tegra_dc *dc = data;
*val = dc->out->ddc_bus;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_dc_ddc_bus_fops, mods_dc_ddc_bus_get,
NULL, "%lld\n");
static int mods_dc_crc_latched_show(struct seq_file *s, void *unused)
{
struct tegra_dc *dc = s->private;
u32 crc = tegra_dc_sysfs_read_checksum_latched(dc);
u32 field = 0;
u32 val;
val = tegra_dc_readl_exported(dc, DC_DISP_INTERLACE_CONTROL);
if (val & INTERLACE_MODE_ENABLE)
field = (val & INTERLACE_STATUS_FIELD_2) ? 1 : 0;
seq_printf(s, "0x%08x %u\n", crc, field);
return 0;
}
static int mods_dc_crc_latched_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_dc_crc_latched_show, inode->i_private);
}
static const struct file_operations mods_dc_crc_latched_fops = {
.open = mods_dc_crc_latched_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* CONFIG_TEGRA_DC */
#if defined(MODS_HAS_TEGRA) && defined(CONFIG_TEGRA_KFUSE)
static int mods_kfuse_show(struct seq_file *s, void *unused)
{
unsigned int buf[KFUSE_DATA_SZ / 4];
/* copy load kfuse into buffer - only needed for early Tegra parts */
int ret = tegra_kfuse_read(buf, sizeof(buf));
int i;
if (ret)
return ret;
for (i = 0; i < KFUSE_DATA_SZ / 4; i += 4)
seq_printf(s, "0x%08x 0x%08x 0x%08x 0x%08x\n",
buf[i], buf[i+1], buf[i+2], buf[i+3]);
return 0;
}
static int mods_kfuse_open(struct inode *inode, struct file *file)
{
return single_open(file, mods_kfuse_show, inode->i_private);
}
static const struct file_operations mods_kfuse_fops = {
.open = mods_kfuse_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* MODS_HAS_TEGRA */
static int mods_debug_get(void *data, u64 *val)
{
*val = (u64)(mods_get_debug_level() & DEBUG_ALL);
return 0;
}
static int mods_debug_set(void *data, u64 val)
{
mods_set_debug_level((int)val & DEBUG_ALL);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_debug_fops, mods_debug_get, mods_debug_set,
"0x%08llx\n");
static int mods_mi_get(void *data, u64 *val)
{
*val = (u64)mods_get_multi_instance();
return 0;
}
static int mods_mi_set(void *data, u64 val)
{
mods_set_multi_instance((int)val);
return 0;
}
#ifdef CONFIG_ARCH_TEGRA_19x_SOC
static int mods_set_err_sel(void *data, u64 val)
{
set_err_sel(val);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_err_sel_fops, 0, mods_set_err_sel, "%llu\n");
static int mods_set_err_ctrl(void *data, u64 val)
{
set_err_ctrl(val);
return 0;
}
static int mods_get_err_ctrl(void *data, u64 *val)
{
*val = get_err_ctrl();
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_err_ctrl_fops, mods_get_err_ctrl,
mods_set_err_ctrl, "%llu\n");
static int mods_enable_cpu_core_reporting(void *data, u64 val)
{
enable_cpu_core_reporting(val);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mods_enable_cpu_fops, 0, mods_enable_cpu_core_reporting,
"%llu\n");
#endif
DEFINE_SIMPLE_ATTRIBUTE(mods_mi_fops, mods_mi_get, mods_mi_set, "%llu\n");
void mods_remove_debugfs(void)
{
debugfs_remove_recursive(mods_debugfs_dir);
mods_debugfs_dir = NULL;
}
int mods_create_debugfs(struct miscdevice *modsdev)
{
#ifdef CONFIG_ARCH_TEGRA_19x_SOC
struct dentry *ras_debugfs_entry;
#endif
struct dentry *retval;
int err = 0;
#ifdef CONFIG_TEGRA_DC
unsigned int dc_idx;
int nheads = tegra_dc_get_numof_dispheads();
int client_reg_rc = 0;
// Check if tegra dc client registration succeeds. This will fail
// with ENODEV if given architecture no longer uses tegra_dc
// driver. Skip creating display related debugfs nodes
bool skip_tegradc_debug_nodes = false;
struct tegra_dc_client client;
memset(&client, 0, sizeof(client));
client_reg_rc = tegra_dc_register_client(&client);
if (client_reg_rc != 0) {
if (client_reg_rc == -ENODEV) {
skip_tegradc_debug_nodes = true;
} else {
pr_err("%s: tegra dc client registration failed\n",
__func__);
return client_reg_rc;
}
} else {
client_reg_rc = tegra_dc_unregister_client(&client);
if (client_reg_rc != 0) {
pr_err("%s: tegra dc client unregistration failed\n",
__func__);
return client_reg_rc;
}
}
#endif
#ifdef CONFIG_TEGRA_DC
if (nheads <= 0 && !skip_tegradc_debug_nodes) {
pr_err("%s: max heads:%d cannot be negative or zero\n",
__func__, nheads);
return -EINVAL;
}
#endif
mods_debugfs_dir = debugfs_create_dir(dev_name(modsdev->this_device),
NULL);
if (IS_ERR(mods_debugfs_dir)) {
err = -EIO;
goto remove_out;
}
#ifdef CONFIG_ARCH_TEGRA_19x_SOC
if (of_find_node_by_name(NULL, "carmel_ras")) {
ras_debugfs_entry = debugfs_create_dir("ras", mods_debugfs_dir);
if (IS_ERR(ras_debugfs_entry)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("err_sel", 0644,
ras_debugfs_entry, 0, &mods_err_sel_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("err_ctrl", 0644,
ras_debugfs_entry, 0, &mods_err_ctrl_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("ccplex_config", 0644,
ras_debugfs_entry, 0, &mods_enable_cpu_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
}
#endif
retval = debugfs_create_file("debug", 0644,
mods_debugfs_dir, 0, &mods_debug_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("multi_instance", 0644,
mods_debugfs_dir, 0, &mods_mi_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
#if defined(MODS_HAS_TEGRA) && defined(CONFIG_TEGRA_KFUSE)
retval = debugfs_create_file("kfuse_data", 0444,
mods_debugfs_dir, 0, &mods_kfuse_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
#endif
#ifdef CONFIG_TEGRA_DC
if (!skip_tegradc_debug_nodes) {
for (dc_idx = 0; dc_idx < nheads; dc_idx++) {
struct dentry *dc_debugfs_dir;
char devname[16];
struct tegra_dc *dc = tegra_dc_get_dc(dc_idx);
if (!dc)
continue;
snprintf(devname, sizeof(devname), "tegradc.%d",
dc->ctrl_num);
dc_debugfs_dir = debugfs_create_dir(devname,
mods_debugfs_dir);
if (IS_ERR(dc_debugfs_dir)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("window_mask", 0444,
dc_debugfs_dir, dc,
&mods_dc_window_mask_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("color_formats", 0444,
dc_debugfs_dir, dc,
&mods_dc_color_formats_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("blend_gen", 0444,
dc_debugfs_dir, dc, &mods_dc_blend_gen_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("layout", 0444,
dc_debugfs_dir, dc, &mods_dc_layout_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("invert", 0444,
dc_debugfs_dir, dc, &mods_dc_invert_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("interlaced", 0444,
dc_debugfs_dir, dc, &mods_dc_interlaced_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("scaling", 0444,
dc_debugfs_dir, dc, &mods_dc_scaling_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("border_color", 0644,
dc_debugfs_dir, dc, &mods_dc_border_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("output_color_possible",
0444, dc_debugfs_dir, NULL, &mods_dc_ocp_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("output_color", 0644,
dc_debugfs_dir, dc, &mods_dc_oc_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("crc_checksum_latched",
0444, dc_debugfs_dir, dc,
&mods_dc_crc_latched_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
if (dc->out && dc->out->type == TEGRA_DC_OUT_DSI) {
struct dentry *dsi_debugfs_dir;
dsi_debugfs_dir = debugfs_create_dir("dsi",
dc_debugfs_dir);
if (IS_ERR(dsi_debugfs_dir)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("ganged", 0444,
dsi_debugfs_dir,
tegra_dc_get_outdata(dc),
&mods_dsi_ganged_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
retval = debugfs_create_file("instance", 0444,
dsi_debugfs_dir,
tegra_dc_get_outdata(dc),
&mods_dsi_inst_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
}
if (dc->out && dc->out->type == TEGRA_DC_OUT_HDMI) {
retval = debugfs_create_file("ddc_bus", 0444,
dc_debugfs_dir, dc,
&mods_dc_ddc_bus_fops);
if (IS_ERR(retval)) {
err = -EIO;
goto remove_out;
}
}
}
}
#endif
return 0;
remove_out:
dev_err(modsdev->this_device, "could not create debugfs\n");
mods_remove_debugfs();
return err;
}