X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Fi915%2Fi915_debugfs.c;h=5fce5d89b68a79c00af6eb42d46dedc336afd287;hb=e8dfcf789591965abf3ad72856137c35abe3edc2;hp=991abff94e1e6116b2d162e5af0138de0b67f3ea;hpb=b2c88f5b1dea77b57759387728917a124eb1c098;p=deliverable%2Flinux.git diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 991abff94e1e..5fce5d89b68a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -53,6 +53,32 @@ static const char *yesno(int v) return v ? "yes" : "no"; } +/* As the drm_debugfs_init() routines are called before dev->dev_private is + * allocated we need to hook into the minor for release. */ +static int +drm_add_fake_info_node(struct drm_minor *minor, + struct dentry *ent, + const void *key) +{ + struct drm_info_node *node; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (node == NULL) { + debugfs_remove(ent); + return -ENOMEM; + } + + node->minor = minor; + node->dent = ent; + node->info_ent = (void *) key; + + mutex_lock(&minor->debugfs_lock); + list_add(&node->list, &minor->debugfs_list); + mutex_unlock(&minor->debugfs_lock); + + return 0; +} + static int i915_capabilities(struct seq_file *m, void *data) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -1734,40 +1760,156 @@ static int i915_pc8_status(struct seq_file *m, void *unused) return 0; } -static int i915_pipe_crc(struct seq_file *m, void *data) +struct pipe_crc_info { + const char *name; + struct drm_device *dev; + enum pipe pipe; +}; + +static int i915_pipe_crc_open(struct inode *inode, struct file *filep) +{ + struct pipe_crc_info *info = inode->i_private; + struct drm_i915_private *dev_priv = info->dev->dev_private; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; + + if (!atomic_dec_and_test(&pipe_crc->available)) { + atomic_inc(&pipe_crc->available); + return -EBUSY; /* already open */ + } + + filep->private_data = inode->i_private; + + return 0; +} + +static int i915_pipe_crc_release(struct inode *inode, struct file *filep) +{ + struct pipe_crc_info *info = inode->i_private; + struct drm_i915_private *dev_priv = info->dev->dev_private; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; + + atomic_inc(&pipe_crc->available); /* release the device */ + + return 0; +} + +/* (6 fields, 8 chars each, space separated (5) + '\n') */ +#define PIPE_CRC_LINE_LEN (6 * 8 + 5 + 1) +/* account for \'0' */ +#define PIPE_CRC_BUFFER_LEN (PIPE_CRC_LINE_LEN + 1) + +static int pipe_crc_data_count(struct intel_pipe_crc *pipe_crc) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - enum pipe pipe = (enum pipe)node->info_ent->data; - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; int head, tail; - if (dev_priv->pipe_crc[pipe].source == INTEL_PIPE_CRC_SOURCE_NONE) { - seq_puts(m, "none\n"); + head = atomic_read(&pipe_crc->head); + tail = atomic_read(&pipe_crc->tail); + + return CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR); +} + +static ssize_t +i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, + loff_t *pos) +{ + struct pipe_crc_info *info = filep->private_data; + struct drm_device *dev = info->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; + char buf[PIPE_CRC_BUFFER_LEN]; + int head, tail, n_entries, n; + ssize_t bytes_read; + + /* + * Don't allow user space to provide buffers not big enough to hold + * a line of data. + */ + if (count < PIPE_CRC_LINE_LEN) + return -EINVAL; + + if (pipe_crc->source == INTEL_PIPE_CRC_SOURCE_NONE) return 0; + + /* nothing to read */ + while (pipe_crc_data_count(pipe_crc) == 0) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(pipe_crc->wq, + pipe_crc_data_count(pipe_crc))) + return -ERESTARTSYS; } - seq_puts(m, " timestamp CRC1 CRC2 CRC3 CRC4 CRC5\n"); + /* We now have one or more entries to read */ head = atomic_read(&pipe_crc->head); tail = atomic_read(&pipe_crc->tail); - - while (CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) >= 1) { + n_entries = min((size_t)CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR), + count / PIPE_CRC_LINE_LEN); + bytes_read = 0; + n = 0; + do { struct intel_pipe_crc_entry *entry = &pipe_crc->entries[tail]; + int ret; - seq_printf(m, "%12u %8x %8x %8x %8x %8x\n", entry->timestamp, - entry->crc[0], entry->crc[1], entry->crc[2], - entry->crc[3], entry->crc[4]); + bytes_read += snprintf(buf, PIPE_CRC_BUFFER_LEN, + "%8u %8x %8x %8x %8x %8x\n", + entry->frame, entry->crc[0], + entry->crc[1], entry->crc[2], + entry->crc[3], entry->crc[4]); + + ret = copy_to_user(user_buf + n * PIPE_CRC_LINE_LEN, + buf, PIPE_CRC_LINE_LEN); + if (ret == PIPE_CRC_LINE_LEN) + return -EFAULT; BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR); tail = (tail + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); atomic_set(&pipe_crc->tail, tail); - } + n++; + } while (--n_entries); - return 0; + return bytes_read; } -static const char *pipe_crc_sources[] = { +static const struct file_operations i915_pipe_crc_fops = { + .owner = THIS_MODULE, + .open = i915_pipe_crc_open, + .read = i915_pipe_crc_read, + .release = i915_pipe_crc_release, +}; + +static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = { + { + .name = "i915_pipe_A_crc", + .pipe = PIPE_A, + }, + { + .name = "i915_pipe_B_crc", + .pipe = PIPE_B, + }, + { + .name = "i915_pipe_C_crc", + .pipe = PIPE_C, + }, +}; + +static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor, + enum pipe pipe) +{ + struct drm_device *dev = minor->dev; + struct dentry *ent; + struct pipe_crc_info *info = &i915_pipe_crc_data[pipe]; + + info->dev = dev; + ent = debugfs_create_file(info->name, S_IRUGO, root, info, + &i915_pipe_crc_fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + + return drm_add_fake_info_node(minor, ent, info); +} + +static const char * const pipe_crc_sources[] = { "none", "plane1", "plane2", @@ -1780,7 +1922,7 @@ static const char *pipe_crc_source_name(enum intel_pipe_crc_source source) return pipe_crc_sources[source]; } -static int pipe_crc_ctl_show(struct seq_file *m, void *data) +static int display_crc_ctl_show(struct seq_file *m, void *data) { struct drm_device *dev = m->private; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1793,26 +1935,46 @@ static int pipe_crc_ctl_show(struct seq_file *m, void *data) return 0; } -static int pipe_crc_ctl_open(struct inode *inode, struct file *file) +static int display_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); + return single_open(file, display_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; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; u32 val; - - return -ENODEV; - if (!IS_IVYBRIDGE(dev)) return -ENODEV; - dev_priv->pipe_crc[pipe].source = source; + if (pipe_crc->source == source) + return 0; + + /* forbid changing the source without going back to 'none' */ + if (pipe_crc->source && source) + return -EINVAL; + + /* none -> real source transition */ + if (source) { + DRM_DEBUG_DRIVER("collecting CRCs for pipe %c, %s\n", + pipe_name(pipe), pipe_crc_source_name(source)); + + pipe_crc->entries = kzalloc(sizeof(*pipe_crc->entries) * + INTEL_PIPE_CRC_ENTRIES_NR, + GFP_KERNEL); + if (!pipe_crc->entries) + return -ENOMEM; + + atomic_set(&pipe_crc->head, 0); + atomic_set(&pipe_crc->tail, 0); + } + + pipe_crc->source = source; switch (source) { case INTEL_PIPE_CRC_SOURCE_PLANE1: @@ -1833,21 +1995,31 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, I915_WRITE(PIPE_CRC_CTL(pipe), val); POSTING_READ(PIPE_CRC_CTL(pipe)); + /* real source -> none transition */ + if (source == INTEL_PIPE_CRC_SOURCE_NONE) { + DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n", + pipe_name(pipe)); + + kfree(pipe_crc->entries); + pipe_crc->entries = NULL; + } + return 0; } /* * Parse pipe CRC command strings: - * command: wsp* pipe wsp+ source wsp* - * pipe: (A | B | C) + * command: wsp* object wsp+ name wsp+ source wsp* + * object: 'pipe' + * name: (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 + * "pipe A plane1" -> Start CRC computations on plane1 of pipe A + * "pipe A none" -> Stop CRC */ -static int pipe_crc_ctl_tokenize(char *buf, char *words[], int max_words) +static int display_crc_ctl_tokenize(char *buf, char *words[], int max_words) { int n_words = 0; @@ -1878,7 +2050,29 @@ static int pipe_crc_ctl_tokenize(char *buf, char *words[], int max_words) return n_words; } -static int pipe_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe) +enum intel_pipe_crc_object { + PIPE_CRC_OBJECT_PIPE, +}; + +static const char * const pipe_crc_objects[] = { + "pipe", +}; + +static int +display_crc_ctl_parse_object(const char *buf, enum intel_pipe_crc_object *o) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pipe_crc_objects); i++) + if (!strcmp(buf, pipe_crc_objects[i])) { + *o = i; + return 0; + } + + return -EINVAL; +} + +static int display_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe) { const char name = buf[0]; @@ -1891,48 +2085,55 @@ static int pipe_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe) } static int -pipe_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *source) +display_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *s) { int i; for (i = 0; i < ARRAY_SIZE(pipe_crc_sources); i++) if (!strcmp(buf, pipe_crc_sources[i])) { - *source = i; + *s = i; return 0; } return -EINVAL; } -static int pipe_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len) +static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len) { -#define MAX_WORDS 2 +#define N_WORDS 3 int n_words; - char *words[MAX_WORDS]; + char *words[N_WORDS]; enum pipe pipe; + enum intel_pipe_crc_object object; 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"); + n_words = display_crc_ctl_tokenize(buf, words, N_WORDS); + if (n_words != N_WORDS) { + DRM_DEBUG_DRIVER("tokenize failed, a command is %d words\n", + N_WORDS); return -EINVAL; } - if (pipe_crc_ctl_parse_pipe(words[0], &pipe) < 0) { - DRM_DEBUG_DRIVER("unknown pipe %s\n", words[0]); + if (display_crc_ctl_parse_object(words[0], &object) < 0) { + DRM_DEBUG_DRIVER("unknown object %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]); + if (display_crc_ctl_parse_pipe(words[1], &pipe) < 0) { + DRM_DEBUG_DRIVER("unknown pipe %s\n", words[1]); + return -EINVAL; + } + + if (display_crc_ctl_parse_source(words[2], &source) < 0) { + DRM_DEBUG_DRIVER("unknown source %s\n", words[2]); 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) +static ssize_t display_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; @@ -1958,7 +2159,7 @@ static ssize_t pipe_crc_ctl_write(struct file *file, const char __user *ubuf, } tmpbuf[len] = '\0'; - ret = pipe_crc_ctl_parse(dev, tmpbuf, len); + ret = display_crc_ctl_parse(dev, tmpbuf, len); out: kfree(tmpbuf); @@ -1969,13 +2170,13 @@ out: return len; } -static const struct file_operations i915_pipe_crc_ctl_fops = { +static const struct file_operations i915_display_crc_ctl_fops = { .owner = THIS_MODULE, - .open = pipe_crc_ctl_open, + .open = display_crc_ctl_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, - .write = pipe_crc_ctl_write + .write = display_crc_ctl_write }; static int @@ -2363,32 +2564,6 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops, i915_cache_sharing_get, i915_cache_sharing_set, "%llu\n"); -/* As the drm_debugfs_init() routines are called before dev->dev_private is - * allocated we need to hook into the minor for release. */ -static int -drm_add_fake_info_node(struct drm_minor *minor, - struct dentry *ent, - const void *key) -{ - struct drm_info_node *node; - - node = kmalloc(sizeof(*node), GFP_KERNEL); - if (node == NULL) { - debugfs_remove(ent); - return -ENOMEM; - } - - node->minor = minor; - node->dent = ent; - node->info_ent = (void *) key; - - mutex_lock(&minor->debugfs_lock); - list_add(&node->list, &minor->debugfs_list); - mutex_unlock(&minor->debugfs_lock); - - return 0; -} - static int i915_forcewake_open(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; @@ -2493,9 +2668,6 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_edp_psr_status", i915_edp_psr_status, 0}, {"i915_energy_uJ", i915_energy_uJ, 0}, {"i915_pc8_status", i915_pc8_status, 0}, - {"i915_pipe_A_crc", i915_pipe_crc, 0, (void *)PIPE_A}, - {"i915_pipe_B_crc", i915_pipe_crc, 0, (void *)PIPE_B}, - {"i915_pipe_C_crc", i915_pipe_crc, 0, (void *)PIPE_C}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) @@ -2513,9 +2685,22 @@ 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}, + {"i915_display_crc_ctl", &i915_display_crc_ctl_fops}, }; +void intel_display_crc_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[i]; + + atomic_set(&pipe_crc->available, 1); + init_waitqueue_head(&pipe_crc->wq); + } +} + int i915_debugfs_init(struct drm_minor *minor) { int ret, i; @@ -2524,6 +2709,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { + ret = i915_pipe_crc_create(minor->debugfs_root, minor, i); + if (ret) + return ret; + } + for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { ret = i915_debugfs_create(minor->debugfs_root, minor, i915_debugfs_files[i].name, @@ -2539,12 +2730,22 @@ int i915_debugfs_init(struct drm_minor *minor) void i915_debugfs_cleanup(struct drm_minor *minor) { + struct drm_device *dev = minor->dev; int i; drm_debugfs_remove_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops, 1, minor); + + for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { + struct drm_info_list *info_list = + (struct drm_info_list *)&i915_pipe_crc_data[i]; + + drm_debugfs_remove_files(info_list, 1, minor); + } + for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { struct drm_info_list *info_list = (struct drm_info_list *) i915_debugfs_files[i].fops;