drm/tegra: sor - Add CRC debugfs support
authorThierry Reding <treding@nvidia.com>
Fri, 31 Jan 2014 09:02:15 +0000 (10:02 +0100)
committerThierry Reding <treding@nvidia.com>
Thu, 5 Jun 2014 21:09:17 +0000 (23:09 +0200)
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>
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tegra/sor.h

index 49ef5729f435daa411c508070a36c3e50a3e7c1a..b2151ea679f0baffb77947acb2d44eccdce4202d 100644 (file)
@@ -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);
index f4156d54cd058d7f9775cf2892c9bdcc19545622..a5f8853fedb5aaf391e794c45ab49f754cb6df9a 100644 (file)
@@ -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)
 
 #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