Commit 926321d5 authored by Daniel Vetter's avatar Daniel Vetter
Browse files

drm/i915: Add a control file for pipe CRCs



Note the "return -ENODEV;" in pipe_crc_set_source(). The ctl file is
disabled until the end of the series to be able to do incremental
improvements.

Signed-off-by: default avatarDamien Lespiau <damien.lespiau@intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 8bf1e9f1
Loading
Loading
Loading
Loading
+215 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
 */

#include <linux/seq_file.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/export.h>
@@ -1742,8 +1743,8 @@ static int i915_pipe_crc(struct seq_file *m, void *data)
	int i;
	int start;

	if (!IS_IVYBRIDGE(dev)) {
		seq_puts(m, "unsupported\n");
	if (dev_priv->pipe_crc[pipe].source == INTEL_PIPE_CRC_SOURCE_NONE) {
		seq_puts(m, "none\n");
		return 0;
	}

@@ -1762,6 +1763,217 @@ static int i915_pipe_crc(struct seq_file *m, void *data)
	return 0;
}

static const char *pipe_crc_sources[] = {
	"none",
	"plane1",
	"plane2",
	"pf",
};

static const char *pipe_crc_source_name(enum intel_pipe_crc_source source)
{
	BUILD_BUG_ON(ARRAY_SIZE(pipe_crc_sources) != INTEL_PIPE_CRC_SOURCE_MAX);
	return pipe_crc_sources[source];
}

static int pipe_crc_ctl_show(struct seq_file *m, void *data)
{
	struct drm_device *dev = m->private;
	struct drm_i915_private *dev_priv = dev->dev_private;
	int i;

	for (i = 0; i < I915_MAX_PIPES; i++)
		seq_printf(m, "%c %s\n", pipe_name(i),
			   pipe_crc_source_name(dev_priv->pipe_crc[i].source));

	return 0;
}

static int pipe_crc_ctl_open(struct inode *inode, struct file *file)
{
	struct drm_device *dev = inode->i_private;

	return single_open(file, pipe_crc_ctl_show, dev);
}

static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
			       enum intel_pipe_crc_source source)
{
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32 val;


	return -ENODEV;

	if (!IS_IVYBRIDGE(dev))
		return -ENODEV;

	dev_priv->pipe_crc[pipe].source = source;

	switch (source) {
	case INTEL_PIPE_CRC_SOURCE_PLANE1:
		val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_IVB;
		break;
	case INTEL_PIPE_CRC_SOURCE_PLANE2:
		val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB;
		break;
	case INTEL_PIPE_CRC_SOURCE_PF:
		val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB;
		break;
	case INTEL_PIPE_CRC_SOURCE_NONE:
	default:
		val = 0;
		break;
	}

	I915_WRITE(PIPE_CRC_CTL(pipe), val);
	POSTING_READ(PIPE_CRC_CTL(pipe));

	return 0;
}

/*
 * Parse pipe CRC command strings:
 *   command: wsp* pipe wsp+ source wsp*
 *   pipe: (A | B | C)
 *   source: (none | plane1 | plane2 | pf)
 *   wsp: (#0x20 | #0x9 | #0xA)+
 *
 * eg.:
 *  "A plane1"  ->  Start CRC computations on plane1 of pipe A
 *  "A none"    ->  Stop CRC
 */
static int pipe_crc_ctl_tokenize(char *buf, char *words[], int max_words)
{
	int n_words = 0;

	while (*buf) {
		char *end;

		/* skip leading white space */
		buf = skip_spaces(buf);
		if (!*buf)
			break;	/* end of buffer */

		/* find end of word */
		for (end = buf; *end && !isspace(*end); end++)
			;

		if (n_words == max_words) {
			DRM_DEBUG_DRIVER("too many words, allowed <= %d\n",
					 max_words);
			return -EINVAL;	/* ran out of words[] before bytes */
		}

		if (*end)
			*end++ = '\0';
		words[n_words++] = buf;
		buf = end;
	}

	return n_words;
}

static int pipe_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe)
{
	const char name = buf[0];

	if (name < 'A' || name >= pipe_name(I915_MAX_PIPES))
		return -EINVAL;

	*pipe = name - 'A';

	return 0;
}

static int
pipe_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *source)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(pipe_crc_sources); i++)
		if (!strcmp(buf, pipe_crc_sources[i])) {
			*source = i;
			return 0;
		    }

	return -EINVAL;
}

static int pipe_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len)
{
#define MAX_WORDS 2
	int n_words;
	char *words[MAX_WORDS];
	enum pipe pipe;
	enum intel_pipe_crc_source source;

	n_words = pipe_crc_ctl_tokenize(buf, words, MAX_WORDS);
	if (n_words != 2) {
		DRM_DEBUG_DRIVER("tokenize failed, a command is 2 words\n");
		return -EINVAL;
	}

	if (pipe_crc_ctl_parse_pipe(words[0], &pipe) < 0) {
		DRM_DEBUG_DRIVER("unknown pipe %s\n", words[0]);
		return -EINVAL;
	}

	if (pipe_crc_ctl_parse_source(words[1], &source) < 0) {
		DRM_DEBUG_DRIVER("unknown source %s\n", words[1]);
		return -EINVAL;
	}

	return pipe_crc_set_source(dev, pipe, source);
}

static ssize_t pipe_crc_ctl_write(struct file *file, const char __user *ubuf,
				  size_t len, loff_t *offp)
{
	struct seq_file *m = file->private_data;
	struct drm_device *dev = m->private;
	char *tmpbuf;
	int ret;

	if (len == 0)
		return 0;

	if (len > PAGE_SIZE - 1) {
		DRM_DEBUG_DRIVER("expected <%lu bytes into pipe crc control\n",
				 PAGE_SIZE);
		return -E2BIG;
	}

	tmpbuf = kmalloc(len + 1, GFP_KERNEL);
	if (!tmpbuf)
		return -ENOMEM;

	if (copy_from_user(tmpbuf, ubuf, len)) {
		ret = -EFAULT;
		goto out;
	}
	tmpbuf[len] = '\0';

	ret = pipe_crc_ctl_parse(dev, tmpbuf, len);

out:
	kfree(tmpbuf);
	if (ret < 0)
		return ret;

	*offp += len;
	return len;
}

static const struct file_operations i915_pipe_crc_ctl_fops = {
	.owner = THIS_MODULE,
	.open = pipe_crc_ctl_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	.write = pipe_crc_ctl_write
};

static int
i915_wedged_get(void *data, u64 *val)
{
@@ -2297,6 +2509,7 @@ static struct i915_debugfs_files {
	{"i915_gem_drop_caches", &i915_drop_caches_fops},
	{"i915_error_state", &i915_error_state_fops},
	{"i915_next_seqno", &i915_next_seqno_fops},
	{"i915_pipe_crc_ctl", &i915_pipe_crc_ctl_fops},
};

int i915_debugfs_init(struct drm_minor *minor)
+9 −0
Original line number Diff line number Diff line
@@ -1217,6 +1217,14 @@ struct i915_package_c8 {
	} regsave;
};

enum intel_pipe_crc_source {
	INTEL_PIPE_CRC_SOURCE_NONE,
	INTEL_PIPE_CRC_SOURCE_PLANE1,
	INTEL_PIPE_CRC_SOURCE_PLANE2,
	INTEL_PIPE_CRC_SOURCE_PF,
	INTEL_PIPE_CRC_SOURCE_MAX,
};

struct intel_pipe_crc_entry {
	uint32_t timestamp;
	uint32_t crc[5];
@@ -1225,6 +1233,7 @@ struct intel_pipe_crc_entry {
#define INTEL_PIPE_CRC_ENTRIES_NR	200
struct intel_pipe_crc {
	struct intel_pipe_crc_entry entries[INTEL_PIPE_CRC_ENTRIES_NR];
	enum intel_pipe_crc_source source;
	atomic_t slot;
};