diff options
author | Damien Lespiau <damien.lespiau@intel.com> | 2013-10-15 13:55:40 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2013-10-16 07:32:16 -0400 |
commit | 071444280bcbb96ec38a1fb1ee3924ca7860844a (patch) | |
tree | 49171943b164c5f1c00fc3418e43500834e9e681 | |
parent | 497666d80587933fc65dbe40d8fe6b6cc89ac9ad (diff) |
drm/i915: Implement blocking read for pipe CRC files
seq_file is not quite the right interface for these ones. We have a
circular buffer with a new entry per vblank on one side and a process
wanting to dequeue the CRC with a read().
It's quite racy to wait for vblank in user land and then try to read a
pipe_crc file, sometimes the CRC interrupt hasn't been fired and we end
up with an EOF.
So, let's have the read on the pipe_crc file block until the interrupt
gives us a new entry. At that point we can wake the reading process.
Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-rw-r--r-- | drivers/gpu/drm/i915/i915_debugfs.c | 164 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 2 |
4 files changed, 155 insertions, 19 deletions
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index baa2e430bd5b..5137f8f97b8a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c | |||
@@ -1760,37 +1760,138 @@ static int i915_pc8_status(struct seq_file *m, void *unused) | |||
1760 | return 0; | 1760 | return 0; |
1761 | } | 1761 | } |
1762 | 1762 | ||
1763 | static int i915_pipe_crc(struct seq_file *m, void *data) | 1763 | struct pipe_crc_info { |
1764 | const char *name; | ||
1765 | struct drm_device *dev; | ||
1766 | enum pipe pipe; | ||
1767 | }; | ||
1768 | |||
1769 | static int i915_pipe_crc_open(struct inode *inode, struct file *filep) | ||
1770 | { | ||
1771 | filep->private_data = inode->i_private; | ||
1772 | |||
1773 | return 0; | ||
1774 | } | ||
1775 | |||
1776 | static int i915_pipe_crc_release(struct inode *inode, struct file *filep) | ||
1777 | { | ||
1778 | return 0; | ||
1779 | } | ||
1780 | |||
1781 | /* (6 fields, 8 chars each, space separated (5) + '\n') */ | ||
1782 | #define PIPE_CRC_LINE_LEN (6 * 8 + 5 + 1) | ||
1783 | /* account for \'0' */ | ||
1784 | #define PIPE_CRC_BUFFER_LEN (PIPE_CRC_LINE_LEN + 1) | ||
1785 | |||
1786 | static int pipe_crc_data_count(struct intel_pipe_crc *pipe_crc) | ||
1764 | { | 1787 | { |
1765 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
1766 | struct drm_device *dev = node->minor->dev; | ||
1767 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
1768 | enum pipe pipe = (enum pipe)node->info_ent->data; | ||
1769 | struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; | ||
1770 | int head, tail; | 1788 | int head, tail; |
1771 | 1789 | ||
1772 | if (dev_priv->pipe_crc[pipe].source == INTEL_PIPE_CRC_SOURCE_NONE) { | 1790 | head = atomic_read(&pipe_crc->head); |
1773 | seq_puts(m, "none\n"); | 1791 | tail = atomic_read(&pipe_crc->tail); |
1792 | |||
1793 | return CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR); | ||
1794 | } | ||
1795 | |||
1796 | static ssize_t | ||
1797 | i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, | ||
1798 | loff_t *pos) | ||
1799 | { | ||
1800 | struct pipe_crc_info *info = filep->private_data; | ||
1801 | struct drm_device *dev = info->dev; | ||
1802 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
1803 | struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; | ||
1804 | char buf[PIPE_CRC_BUFFER_LEN]; | ||
1805 | int head, tail, n_entries, n; | ||
1806 | ssize_t bytes_read; | ||
1807 | |||
1808 | /* | ||
1809 | * Don't allow user space to provide buffers not big enough to hold | ||
1810 | * a line of data. | ||
1811 | */ | ||
1812 | if (count < PIPE_CRC_LINE_LEN) | ||
1813 | return -EINVAL; | ||
1814 | |||
1815 | if (pipe_crc->source == INTEL_PIPE_CRC_SOURCE_NONE) | ||
1774 | return 0; | 1816 | return 0; |
1817 | |||
1818 | /* nothing to read */ | ||
1819 | while (pipe_crc_data_count(pipe_crc) == 0) { | ||
1820 | if (filep->f_flags & O_NONBLOCK) | ||
1821 | return -EAGAIN; | ||
1822 | |||
1823 | if (wait_event_interruptible(pipe_crc->wq, | ||
1824 | pipe_crc_data_count(pipe_crc))) | ||
1825 | return -ERESTARTSYS; | ||
1775 | } | 1826 | } |
1776 | 1827 | ||
1777 | seq_puts(m, " frame CRC1 CRC2 CRC3 CRC4 CRC5\n"); | 1828 | /* We now have one or more entries to read */ |
1778 | head = atomic_read(&pipe_crc->head); | 1829 | head = atomic_read(&pipe_crc->head); |
1779 | tail = atomic_read(&pipe_crc->tail); | 1830 | tail = atomic_read(&pipe_crc->tail); |
1780 | 1831 | n_entries = min((size_t)CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR), | |
1781 | while (CIRC_CNT(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) >= 1) { | 1832 | count / PIPE_CRC_LINE_LEN); |
1833 | bytes_read = 0; | ||
1834 | n = 0; | ||
1835 | do { | ||
1782 | struct intel_pipe_crc_entry *entry = &pipe_crc->entries[tail]; | 1836 | struct intel_pipe_crc_entry *entry = &pipe_crc->entries[tail]; |
1837 | int ret; | ||
1783 | 1838 | ||
1784 | seq_printf(m, "%8u %8x %8x %8x %8x %8x\n", entry->frame, | 1839 | bytes_read += snprintf(buf, PIPE_CRC_BUFFER_LEN, |
1785 | entry->crc[0], entry->crc[1], entry->crc[2], | 1840 | "%8u %8x %8x %8x %8x %8x\n", |
1786 | entry->crc[3], entry->crc[4]); | 1841 | entry->frame, entry->crc[0], |
1842 | entry->crc[1], entry->crc[2], | ||
1843 | entry->crc[3], entry->crc[4]); | ||
1844 | |||
1845 | ret = copy_to_user(user_buf + n * PIPE_CRC_LINE_LEN, | ||
1846 | buf, PIPE_CRC_LINE_LEN); | ||
1847 | if (ret == PIPE_CRC_LINE_LEN) | ||
1848 | return -EFAULT; | ||
1787 | 1849 | ||
1788 | BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR); | 1850 | BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR); |
1789 | tail = (tail + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); | 1851 | tail = (tail + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); |
1790 | atomic_set(&pipe_crc->tail, tail); | 1852 | atomic_set(&pipe_crc->tail, tail); |
1791 | } | 1853 | n++; |
1854 | } while (--n_entries); | ||
1792 | 1855 | ||
1793 | return 0; | 1856 | return bytes_read; |
1857 | } | ||
1858 | |||
1859 | static const struct file_operations i915_pipe_crc_fops = { | ||
1860 | .owner = THIS_MODULE, | ||
1861 | .open = i915_pipe_crc_open, | ||
1862 | .read = i915_pipe_crc_read, | ||
1863 | .release = i915_pipe_crc_release, | ||
1864 | }; | ||
1865 | |||
1866 | static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = { | ||
1867 | { | ||
1868 | .name = "i915_pipe_A_crc", | ||
1869 | .pipe = PIPE_A, | ||
1870 | }, | ||
1871 | { | ||
1872 | .name = "i915_pipe_B_crc", | ||
1873 | .pipe = PIPE_B, | ||
1874 | }, | ||
1875 | { | ||
1876 | .name = "i915_pipe_C_crc", | ||
1877 | .pipe = PIPE_C, | ||
1878 | }, | ||
1879 | }; | ||
1880 | |||
1881 | static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor, | ||
1882 | enum pipe pipe) | ||
1883 | { | ||
1884 | struct drm_device *dev = minor->dev; | ||
1885 | struct dentry *ent; | ||
1886 | struct pipe_crc_info *info = &i915_pipe_crc_data[pipe]; | ||
1887 | |||
1888 | info->dev = dev; | ||
1889 | ent = debugfs_create_file(info->name, S_IRUGO, root, info, | ||
1890 | &i915_pipe_crc_fops); | ||
1891 | if (IS_ERR(ent)) | ||
1892 | return PTR_ERR(ent); | ||
1893 | |||
1894 | return drm_add_fake_info_node(minor, ent, info); | ||
1794 | } | 1895 | } |
1795 | 1896 | ||
1796 | static const char *pipe_crc_sources[] = { | 1897 | static const char *pipe_crc_sources[] = { |
@@ -2555,9 +2656,6 @@ static struct drm_info_list i915_debugfs_list[] = { | |||
2555 | {"i915_edp_psr_status", i915_edp_psr_status, 0}, | 2656 | {"i915_edp_psr_status", i915_edp_psr_status, 0}, |
2556 | {"i915_energy_uJ", i915_energy_uJ, 0}, | 2657 | {"i915_energy_uJ", i915_energy_uJ, 0}, |
2557 | {"i915_pc8_status", i915_pc8_status, 0}, | 2658 | {"i915_pc8_status", i915_pc8_status, 0}, |
2558 | {"i915_pipe_A_crc", i915_pipe_crc, 0, (void *)PIPE_A}, | ||
2559 | {"i915_pipe_B_crc", i915_pipe_crc, 0, (void *)PIPE_B}, | ||
2560 | {"i915_pipe_C_crc", i915_pipe_crc, 0, (void *)PIPE_C}, | ||
2561 | }; | 2659 | }; |
2562 | #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) | 2660 | #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) |
2563 | 2661 | ||
@@ -2578,6 +2676,18 @@ static struct i915_debugfs_files { | |||
2578 | {"i915_display_crc_ctl", &i915_display_crc_ctl_fops}, | 2676 | {"i915_display_crc_ctl", &i915_display_crc_ctl_fops}, |
2579 | }; | 2677 | }; |
2580 | 2678 | ||
2679 | void intel_display_crc_init(struct drm_device *dev) | ||
2680 | { | ||
2681 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
2682 | int i; | ||
2683 | |||
2684 | for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { | ||
2685 | struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[i]; | ||
2686 | |||
2687 | init_waitqueue_head(&pipe_crc->wq); | ||
2688 | } | ||
2689 | } | ||
2690 | |||
2581 | int i915_debugfs_init(struct drm_minor *minor) | 2691 | int i915_debugfs_init(struct drm_minor *minor) |
2582 | { | 2692 | { |
2583 | int ret, i; | 2693 | int ret, i; |
@@ -2586,6 +2696,12 @@ int i915_debugfs_init(struct drm_minor *minor) | |||
2586 | if (ret) | 2696 | if (ret) |
2587 | return ret; | 2697 | return ret; |
2588 | 2698 | ||
2699 | for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { | ||
2700 | ret = i915_pipe_crc_create(minor->debugfs_root, minor, i); | ||
2701 | if (ret) | ||
2702 | return ret; | ||
2703 | } | ||
2704 | |||
2589 | for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { | 2705 | for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { |
2590 | ret = i915_debugfs_create(minor->debugfs_root, minor, | 2706 | ret = i915_debugfs_create(minor->debugfs_root, minor, |
2591 | i915_debugfs_files[i].name, | 2707 | i915_debugfs_files[i].name, |
@@ -2601,12 +2717,22 @@ int i915_debugfs_init(struct drm_minor *minor) | |||
2601 | 2717 | ||
2602 | void i915_debugfs_cleanup(struct drm_minor *minor) | 2718 | void i915_debugfs_cleanup(struct drm_minor *minor) |
2603 | { | 2719 | { |
2720 | struct drm_device *dev = minor->dev; | ||
2604 | int i; | 2721 | int i; |
2605 | 2722 | ||
2606 | drm_debugfs_remove_files(i915_debugfs_list, | 2723 | drm_debugfs_remove_files(i915_debugfs_list, |
2607 | I915_DEBUGFS_ENTRIES, minor); | 2724 | I915_DEBUGFS_ENTRIES, minor); |
2725 | |||
2608 | drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops, | 2726 | drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops, |
2609 | 1, minor); | 2727 | 1, minor); |
2728 | |||
2729 | for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { | ||
2730 | struct drm_info_list *info_list = | ||
2731 | (struct drm_info_list *)&i915_pipe_crc_data[i]; | ||
2732 | |||
2733 | drm_debugfs_remove_files(info_list, 1, minor); | ||
2734 | } | ||
2735 | |||
2610 | for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { | 2736 | for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { |
2611 | struct drm_info_list *info_list = | 2737 | struct drm_info_list *info_list = |
2612 | (struct drm_info_list *) i915_debugfs_files[i].fops; | 2738 | (struct drm_info_list *) i915_debugfs_files[i].fops; |
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 42cddc11c23c..9f71bc204e38 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c | |||
@@ -1514,6 +1514,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) | |||
1514 | dev_priv->pc8.disable_count = 2; /* requirements_met + gpu_idle */ | 1514 | dev_priv->pc8.disable_count = 2; /* requirements_met + gpu_idle */ |
1515 | INIT_DELAYED_WORK(&dev_priv->pc8.enable_work, hsw_enable_pc8_work); | 1515 | INIT_DELAYED_WORK(&dev_priv->pc8.enable_work, hsw_enable_pc8_work); |
1516 | 1516 | ||
1517 | intel_display_crc_init(dev); | ||
1518 | |||
1517 | i915_dump_device_info(dev_priv); | 1519 | i915_dump_device_info(dev_priv); |
1518 | 1520 | ||
1519 | /* Not all pre-production machines fall into this category, only the | 1521 | /* Not all pre-production machines fall into this category, only the |
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1faeaac5f9f3..7408f1184347 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
@@ -1235,6 +1235,7 @@ struct intel_pipe_crc { | |||
1235 | struct intel_pipe_crc_entry *entries; | 1235 | struct intel_pipe_crc_entry *entries; |
1236 | enum intel_pipe_crc_source source; | 1236 | enum intel_pipe_crc_source source; |
1237 | atomic_t head, tail; | 1237 | atomic_t head, tail; |
1238 | wait_queue_head_t wq; | ||
1238 | }; | 1239 | }; |
1239 | 1240 | ||
1240 | typedef struct drm_i915_private { | 1241 | typedef struct drm_i915_private { |
@@ -2233,6 +2234,11 @@ int i915_verify_lists(struct drm_device *dev); | |||
2233 | /* i915_debugfs.c */ | 2234 | /* i915_debugfs.c */ |
2234 | int i915_debugfs_init(struct drm_minor *minor); | 2235 | int i915_debugfs_init(struct drm_minor *minor); |
2235 | void i915_debugfs_cleanup(struct drm_minor *minor); | 2236 | void i915_debugfs_cleanup(struct drm_minor *minor); |
2237 | #if defined(CONFIG_DEBUG_FS) | ||
2238 | void intel_display_crc_init(struct drm_device *dev); | ||
2239 | #else | ||
2240 | void intel_display_crc_init(struct drm_device *dev) {} | ||
2241 | #endif | ||
2236 | 2242 | ||
2237 | /* i915_gpu_error.c */ | 2243 | /* i915_gpu_error.c */ |
2238 | __printf(2, 3) | 2244 | __printf(2, 3) |
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b201a214279e..b2be05791b2f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -1221,6 +1221,8 @@ static void ivb_pipe_crc_update(struct drm_device *dev, enum pipe pipe) | |||
1221 | 1221 | ||
1222 | head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); | 1222 | head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); |
1223 | atomic_set(&pipe_crc->head, head); | 1223 | atomic_set(&pipe_crc->head, head); |
1224 | |||
1225 | wake_up_interruptible(&pipe_crc->wq); | ||
1224 | } | 1226 | } |
1225 | #else | 1227 | #else |
1226 | static void ivb_pipe_crc_update(struct drm_device *dev, int pipe) {} | 1228 | static void ivb_pipe_crc_update(struct drm_device *dev, int pipe) {} |