mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
drm/tegra: sor - Add CRC debugfs support
The SOR allows the computation of a 32 bit CRC of the content that it transmits. This functionality is exposed via debugfs and is useful to verify proper operation of the SOR. Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
f925390efc
commit
a82752e199
2 changed files with 125 additions and 0 deletions
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
@ -34,6 +35,8 @@ struct tegra_sor {
|
|||
struct tegra_dpaux *dpaux;
|
||||
|
||||
bool enabled;
|
||||
|
||||
struct dentry *debugfs;
|
||||
};
|
||||
|
||||
static inline struct tegra_sor *
|
||||
|
@ -914,6 +917,110 @@ static const struct tegra_output_ops sor_ops = {
|
|||
.detect = tegra_output_sor_detect,
|
||||
};
|
||||
|
||||
static int tegra_sor_crc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_sor_crc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout);
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
value = tegra_sor_readl(sor, SOR_CRC_A);
|
||||
if (value & SOR_CRC_A_VALID)
|
||||
return 0;
|
||||
|
||||
usleep_range(100, 200);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
|
||||
size_t size, loff_t *ppos)
|
||||
{
|
||||
struct tegra_sor *sor = file->private_data;
|
||||
char buf[10];
|
||||
ssize_t num;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
value = tegra_sor_readl(sor, SOR_STATE_1);
|
||||
value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
|
||||
tegra_sor_writel(sor, value, SOR_STATE_1);
|
||||
|
||||
value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
|
||||
value |= SOR_CRC_CNTRL_ENABLE;
|
||||
tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
|
||||
|
||||
value = tegra_sor_readl(sor, SOR_TEST);
|
||||
value &= ~SOR_TEST_CRC_POST_SERIALIZE;
|
||||
tegra_sor_writel(sor, value, SOR_TEST);
|
||||
|
||||
err = tegra_sor_crc_wait(sor, 100);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
|
||||
value = tegra_sor_readl(sor, SOR_CRC_B);
|
||||
|
||||
num = scnprintf(buf, sizeof(buf), "%08x\n", value);
|
||||
|
||||
return simple_read_from_buffer(buffer, size, ppos, buf, num);
|
||||
}
|
||||
|
||||
static const struct file_operations tegra_sor_crc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = tegra_sor_crc_open,
|
||||
.read = tegra_sor_crc_read,
|
||||
.release = tegra_sor_crc_release,
|
||||
};
|
||||
|
||||
static int tegra_sor_debugfs_init(struct tegra_sor *sor, struct dentry *root)
|
||||
{
|
||||
struct dentry *entry;
|
||||
int err = 0;
|
||||
|
||||
sor->debugfs = debugfs_create_dir("sor", root);
|
||||
if (!sor->debugfs)
|
||||
return -ENOMEM;
|
||||
|
||||
entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
|
||||
&tegra_sor_crc_fops);
|
||||
if (!entry) {
|
||||
dev_err(sor->dev,
|
||||
"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
|
||||
root->d_name.name);
|
||||
err = -ENOMEM;
|
||||
goto remove;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
remove:
|
||||
debugfs_remove(sor->debugfs);
|
||||
sor->debugfs = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
|
||||
{
|
||||
debugfs_remove(sor->debugfs);
|
||||
sor->debugfs = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_sor_init(struct host1x_client *client)
|
||||
{
|
||||
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
|
||||
|
@ -934,6 +1041,14 @@ static int tegra_sor_init(struct host1x_client *client)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
struct dentry *root = tegra->drm->primary->debugfs_root;
|
||||
|
||||
err = tegra_sor_debugfs_init(sor, root);
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "debugfs setup failed: %d\n", err);
|
||||
}
|
||||
|
||||
if (sor->dpaux) {
|
||||
err = tegra_dpaux_attach(sor->dpaux, &sor->output);
|
||||
if (err < 0) {
|
||||
|
@ -964,6 +1079,12 @@ static int tegra_sor_exit(struct host1x_client *client)
|
|||
}
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
err = tegra_sor_debugfs_exit(sor);
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
|
||||
}
|
||||
|
||||
err = tegra_output_exit(&sor->output);
|
||||
if (err < 0) {
|
||||
dev_err(sor->dev, "output cleanup failed: %d\n", err);
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#define SOR_HEAD_STATE_4(x) (0x0d + (x))
|
||||
#define SOR_HEAD_STATE_5(x) (0x0f + (x))
|
||||
#define SOR_CRC_CNTRL 0x11
|
||||
#define SOR_CRC_CNTRL_ENABLE (1 << 0)
|
||||
#define SOR_DP_DEBUG_MVID 0x12
|
||||
|
||||
#define SOR_CLK_CNTRL 0x13
|
||||
|
@ -69,6 +70,7 @@
|
|||
#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
|
||||
|
||||
#define SOR_TEST 0x16
|
||||
#define SOR_TEST_CRC_POST_SERIALIZE (1 << 23)
|
||||
#define SOR_TEST_ATTACHED (1 << 10)
|
||||
#define SOR_TEST_HEAD_MODE_MASK (3 << 8)
|
||||
#define SOR_TEST_HEAD_MODE_AWAKE (2 << 8)
|
||||
|
@ -115,6 +117,8 @@
|
|||
|
||||
#define SOR_LVDS 0x1c
|
||||
#define SOR_CRC_A 0x1d
|
||||
#define SOR_CRC_A_VALID (1 << 0)
|
||||
#define SOR_CRC_A_RESET (1 << 0)
|
||||
#define SOR_CRC_B 0x1e
|
||||
#define SOR_BLANK 0x1f
|
||||
#define SOR_SEQ_CTL 0x20
|
||||
|
|
Loading…
Reference in a new issue