lkml.org 
[lkml]   [2016]   [Jul]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 1/3] drm/i915/debugfs: Move out pipe CRC code
    Date
    In preparation to using a generic API in the DRM core for continuous CRC
    generation, move the related code out of i915_debugfs.c into a new file.

    Eventually, only the Intel-specific code will remain in this new file.

    v2: Rebased.

    Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
    ---

    drivers/gpu/drm/i915/Makefile | 2 +-
    drivers/gpu/drm/i915/i915_debugfs.c | 892 +------------------------------
    drivers/gpu/drm/i915/intel_drv.h | 5 +
    drivers/gpu/drm/i915/intel_pipe_crc.c | 951 ++++++++++++++++++++++++++++++++++
    4 files changed, 961 insertions(+), 889 deletions(-)
    create mode 100644 drivers/gpu/drm/i915/intel_pipe_crc.c

    diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
    index 684fc1cd08fa..9419e0c2e2c0 100644
    --- a/drivers/gpu/drm/i915/Makefile
    +++ b/drivers/gpu/drm/i915/Makefile
    @@ -19,7 +19,7 @@ i915-y := i915_drv.o \
    intel_runtime_pm.o

    i915-$(CONFIG_COMPAT) += i915_ioc32.o
    -i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o
    +i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o intel_pipe_crc.o

    # GEM code
    i915-y += i915_cmd_parser.o \
    diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
    index a59e0caeda64..c37bfda32e53 100644
    --- a/drivers/gpu/drm/i915/i915_debugfs.c
    +++ b/drivers/gpu/drm/i915/i915_debugfs.c
    @@ -26,19 +26,9 @@
    *
    */

    -#include <linux/seq_file.h>
    -#include <linux/circ_buf.h>
    -#include <linux/ctype.h>
    #include <linux/debugfs.h>
    -#include <linux/slab.h>
    -#include <linux/export.h>
    #include <linux/list_sort.h>
    -#include <asm/msr-index.h>
    -#include <drm/drmP.h>
    #include "intel_drv.h"
    -#include "intel_ringbuffer.h"
    -#include <drm/i915_drm.h>
    -#include "i915_drv.h"

    enum {
    ACTIVE_LIST,
    @@ -3490,12 +3480,6 @@ static int i915_drrs_status(struct seq_file *m, void *unused)
    return 0;
    }

    -struct pipe_crc_info {
    - const char *name;
    - struct drm_device *dev;
    - enum pipe pipe;
    -};
    -
    static int i915_dp_mst_info(struct seq_file *m, void *unused)
    {
    struct drm_info_node *node = (struct drm_info_node *) m->private;
    @@ -3525,853 +3509,6 @@ static int i915_dp_mst_info(struct seq_file *m, void *unused)
    return 0;
    }

    -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 = to_i915(info->dev);
    - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
    -
    - if (info->pipe >= INTEL_INFO(info->dev)->num_pipes)
    - return -ENODEV;
    -
    - spin_lock_irq(&pipe_crc->lock);
    -
    - if (pipe_crc->opened) {
    - spin_unlock_irq(&pipe_crc->lock);
    - return -EBUSY; /* already open */
    - }
    -
    - pipe_crc->opened = true;
    - filep->private_data = inode->i_private;
    -
    - spin_unlock_irq(&pipe_crc->lock);
    -
    - 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 = to_i915(info->dev);
    - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
    -
    - spin_lock_irq(&pipe_crc->lock);
    - pipe_crc->opened = false;
    - spin_unlock_irq(&pipe_crc->lock);
    -
    - 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)
    -{
    - assert_spin_locked(&pipe_crc->lock);
    - return CIRC_CNT(pipe_crc->head, pipe_crc->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 = to_i915(dev);
    - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
    - char buf[PIPE_CRC_BUFFER_LEN];
    - int n_entries;
    - 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 */
    - spin_lock_irq(&pipe_crc->lock);
    - while (pipe_crc_data_count(pipe_crc) == 0) {
    - int ret;
    -
    - if (filep->f_flags & O_NONBLOCK) {
    - spin_unlock_irq(&pipe_crc->lock);
    - return -EAGAIN;
    - }
    -
    - ret = wait_event_interruptible_lock_irq(pipe_crc->wq,
    - pipe_crc_data_count(pipe_crc), pipe_crc->lock);
    - if (ret) {
    - spin_unlock_irq(&pipe_crc->lock);
    - return ret;
    - }
    - }
    -
    - /* We now have one or more entries to read */
    - n_entries = count / PIPE_CRC_LINE_LEN;
    -
    - bytes_read = 0;
    - while (n_entries > 0) {
    - struct intel_pipe_crc_entry *entry =
    - &pipe_crc->entries[pipe_crc->tail];
    - int ret;
    -
    - if (CIRC_CNT(pipe_crc->head, pipe_crc->tail,
    - INTEL_PIPE_CRC_ENTRIES_NR) < 1)
    - break;
    -
    - BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR);
    - pipe_crc->tail = (pipe_crc->tail + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1);
    -
    - 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]);
    -
    - spin_unlock_irq(&pipe_crc->lock);
    -
    - ret = copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN);
    - if (ret == PIPE_CRC_LINE_LEN)
    - return -EFAULT;
    -
    - user_buf += PIPE_CRC_LINE_LEN;
    - n_entries--;
    -
    - spin_lock_irq(&pipe_crc->lock);
    - }
    -
    - spin_unlock_irq(&pipe_crc->lock);
    -
    - return bytes_read;
    -}
    -
    -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 (!ent)
    - return -ENOMEM;
    -
    - return drm_add_fake_info_node(minor, ent, info);
    -}
    -
    -static const char * const pipe_crc_sources[] = {
    - "none",
    - "plane1",
    - "plane2",
    - "pf",
    - "pipe",
    - "TV",
    - "DP-B",
    - "DP-C",
    - "DP-D",
    - "auto",
    -};
    -
    -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 display_crc_ctl_show(struct seq_file *m, void *data)
    -{
    - struct drm_device *dev = m->private;
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - 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 display_crc_ctl_open(struct inode *inode, struct file *file)
    -{
    - struct drm_device *dev = inode->i_private;
    -
    - return single_open(file, display_crc_ctl_show, dev);
    -}
    -
    -static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
    - uint32_t *val)
    -{
    - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
    - *source = INTEL_PIPE_CRC_SOURCE_PIPE;
    -
    - switch (*source) {
    - case INTEL_PIPE_CRC_SOURCE_PIPE:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_INCLUDE_BORDER_I8XX;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_NONE:
    - *val = 0;
    - break;
    - default:
    - return -EINVAL;
    - }
    -
    - return 0;
    -}
    -
    -static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
    - enum intel_pipe_crc_source *source)
    -{
    - struct intel_encoder *encoder;
    - struct intel_crtc *crtc;
    - struct intel_digital_port *dig_port;
    - int ret = 0;
    -
    - *source = INTEL_PIPE_CRC_SOURCE_PIPE;
    -
    - drm_modeset_lock_all(dev);
    - for_each_intel_encoder(dev, encoder) {
    - if (!encoder->base.crtc)
    - continue;
    -
    - crtc = to_intel_crtc(encoder->base.crtc);
    -
    - if (crtc->pipe != pipe)
    - continue;
    -
    - switch (encoder->type) {
    - case INTEL_OUTPUT_TVOUT:
    - *source = INTEL_PIPE_CRC_SOURCE_TV;
    - break;
    - case INTEL_OUTPUT_DISPLAYPORT:
    - case INTEL_OUTPUT_EDP:
    - dig_port = enc_to_dig_port(&encoder->base);
    - switch (dig_port->port) {
    - case PORT_B:
    - *source = INTEL_PIPE_CRC_SOURCE_DP_B;
    - break;
    - case PORT_C:
    - *source = INTEL_PIPE_CRC_SOURCE_DP_C;
    - break;
    - case PORT_D:
    - *source = INTEL_PIPE_CRC_SOURCE_DP_D;
    - break;
    - default:
    - WARN(1, "nonexisting DP port %c\n",
    - port_name(dig_port->port));
    - break;
    - }
    - break;
    - default:
    - break;
    - }
    - }
    - drm_modeset_unlock_all(dev);
    -
    - return ret;
    -}
    -
    -static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
    - enum pipe pipe,
    - enum intel_pipe_crc_source *source,
    - uint32_t *val)
    -{
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - bool need_stable_symbols = false;
    -
    - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
    - int ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
    - if (ret)
    - return ret;
    - }
    -
    - switch (*source) {
    - case INTEL_PIPE_CRC_SOURCE_PIPE:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_VLV;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_DP_B:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_VLV;
    - need_stable_symbols = true;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_DP_C:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_VLV;
    - need_stable_symbols = true;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_DP_D:
    - if (!IS_CHERRYVIEW(dev))
    - return -EINVAL;
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_VLV;
    - need_stable_symbols = true;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_NONE:
    - *val = 0;
    - break;
    - default:
    - return -EINVAL;
    - }
    -
    - /*
    - * When the pipe CRC tap point is after the transcoders we need
    - * to tweak symbol-level features to produce a deterministic series of
    - * symbols for a given frame. We need to reset those features only once
    - * a frame (instead of every nth symbol):
    - * - DC-balance: used to ensure a better clock recovery from the data
    - * link (SDVO)
    - * - DisplayPort scrambling: used for EMI reduction
    - */
    - if (need_stable_symbols) {
    - uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    -
    - tmp |= DC_BALANCE_RESET_VLV;
    - switch (pipe) {
    - case PIPE_A:
    - tmp |= PIPE_A_SCRAMBLE_RESET;
    - break;
    - case PIPE_B:
    - tmp |= PIPE_B_SCRAMBLE_RESET;
    - break;
    - case PIPE_C:
    - tmp |= PIPE_C_SCRAMBLE_RESET;
    - break;
    - default:
    - return -EINVAL;
    - }
    - I915_WRITE(PORT_DFT2_G4X, tmp);
    - }
    -
    - return 0;
    -}
    -
    -static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
    - enum pipe pipe,
    - enum intel_pipe_crc_source *source,
    - uint32_t *val)
    -{
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - bool need_stable_symbols = false;
    -
    - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
    - int ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
    - if (ret)
    - return ret;
    - }
    -
    - switch (*source) {
    - case INTEL_PIPE_CRC_SOURCE_PIPE:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_TV:
    - if (!SUPPORTS_TV(dev))
    - return -EINVAL;
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_DP_B:
    - if (!IS_G4X(dev))
    - return -EINVAL;
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X;
    - need_stable_symbols = true;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_DP_C:
    - if (!IS_G4X(dev))
    - return -EINVAL;
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X;
    - need_stable_symbols = true;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_DP_D:
    - if (!IS_G4X(dev))
    - return -EINVAL;
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X;
    - need_stable_symbols = true;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_NONE:
    - *val = 0;
    - break;
    - default:
    - return -EINVAL;
    - }
    -
    - /*
    - * When the pipe CRC tap point is after the transcoders we need
    - * to tweak symbol-level features to produce a deterministic series of
    - * symbols for a given frame. We need to reset those features only once
    - * a frame (instead of every nth symbol):
    - * - DC-balance: used to ensure a better clock recovery from the data
    - * link (SDVO)
    - * - DisplayPort scrambling: used for EMI reduction
    - */
    - if (need_stable_symbols) {
    - uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    -
    - WARN_ON(!IS_G4X(dev));
    -
    - I915_WRITE(PORT_DFT_I9XX,
    - I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET);
    -
    - if (pipe == PIPE_A)
    - tmp |= PIPE_A_SCRAMBLE_RESET;
    - else
    - tmp |= PIPE_B_SCRAMBLE_RESET;
    -
    - I915_WRITE(PORT_DFT2_G4X, tmp);
    - }
    -
    - return 0;
    -}
    -
    -static void vlv_undo_pipe_scramble_reset(struct drm_device *dev,
    - enum pipe pipe)
    -{
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    -
    - switch (pipe) {
    - case PIPE_A:
    - tmp &= ~PIPE_A_SCRAMBLE_RESET;
    - break;
    - case PIPE_B:
    - tmp &= ~PIPE_B_SCRAMBLE_RESET;
    - break;
    - case PIPE_C:
    - tmp &= ~PIPE_C_SCRAMBLE_RESET;
    - break;
    - default:
    - return;
    - }
    - if (!(tmp & PIPE_SCRAMBLE_RESET_MASK))
    - tmp &= ~DC_BALANCE_RESET_VLV;
    - I915_WRITE(PORT_DFT2_G4X, tmp);
    -
    -}
    -
    -static void g4x_undo_pipe_scramble_reset(struct drm_device *dev,
    - enum pipe pipe)
    -{
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    -
    - if (pipe == PIPE_A)
    - tmp &= ~PIPE_A_SCRAMBLE_RESET;
    - else
    - tmp &= ~PIPE_B_SCRAMBLE_RESET;
    - I915_WRITE(PORT_DFT2_G4X, tmp);
    -
    - if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) {
    - I915_WRITE(PORT_DFT_I9XX,
    - I915_READ(PORT_DFT_I9XX) & ~DC_BALANCE_RESET);
    - }
    -}
    -
    -static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
    - uint32_t *val)
    -{
    - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
    - *source = INTEL_PIPE_CRC_SOURCE_PIPE;
    -
    - switch (*source) {
    - case INTEL_PIPE_CRC_SOURCE_PLANE1:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_ILK;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_PLANE2:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_ILK;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_PIPE:
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_ILK;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_NONE:
    - *val = 0;
    - break;
    - default:
    - return -EINVAL;
    - }
    -
    - return 0;
    -}
    -
    -static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev, bool enable)
    -{
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - struct intel_crtc *crtc =
    - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]);
    - struct intel_crtc_state *pipe_config;
    - struct drm_atomic_state *state;
    - int ret = 0;
    -
    - drm_modeset_lock_all(dev);
    - state = drm_atomic_state_alloc(dev);
    - if (!state) {
    - ret = -ENOMEM;
    - goto out;
    - }
    -
    - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(&crtc->base);
    - pipe_config = intel_atomic_get_crtc_state(state, crtc);
    - if (IS_ERR(pipe_config)) {
    - ret = PTR_ERR(pipe_config);
    - goto out;
    - }
    -
    - pipe_config->pch_pfit.force_thru = enable;
    - if (pipe_config->cpu_transcoder == TRANSCODER_EDP &&
    - pipe_config->pch_pfit.enabled != enable)
    - pipe_config->base.connectors_changed = true;
    -
    - ret = drm_atomic_commit(state);
    -out:
    - drm_modeset_unlock_all(dev);
    - WARN(ret, "Toggling workaround to %i returns %i\n", enable, ret);
    - if (ret)
    - drm_atomic_state_free(state);
    -}
    -
    -static int ivb_pipe_crc_ctl_reg(struct drm_device *dev,
    - enum pipe pipe,
    - enum intel_pipe_crc_source *source,
    - uint32_t *val)
    -{
    - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
    - *source = INTEL_PIPE_CRC_SOURCE_PF;
    -
    - 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:
    - if (IS_HASWELL(dev) && pipe == PIPE_A)
    - hsw_trans_edp_pipe_A_crc_wa(dev, true);
    -
    - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB;
    - break;
    - case INTEL_PIPE_CRC_SOURCE_NONE:
    - *val = 0;
    - break;
    - default:
    - return -EINVAL;
    - }
    -
    - return 0;
    -}
    -
    -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 = to_i915(dev);
    - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
    - struct intel_crtc *crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev,
    - pipe));
    - enum intel_display_power_domain power_domain;
    - u32 val = 0; /* shut up gcc */
    - int ret;
    -
    - if (pipe_crc->source == source)
    - return 0;
    -
    - /* forbid changing the source without going back to 'none' */
    - if (pipe_crc->source && source)
    - return -EINVAL;
    -
    - power_domain = POWER_DOMAIN_PIPE(pipe);
    - if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) {
    - DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n");
    - return -EIO;
    - }
    -
    - if (IS_GEN2(dev))
    - ret = i8xx_pipe_crc_ctl_reg(&source, &val);
    - else if (INTEL_INFO(dev)->gen < 5)
    - ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val);
    - else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
    - ret = vlv_pipe_crc_ctl_reg(dev, pipe, &source, &val);
    - else if (IS_GEN5(dev) || IS_GEN6(dev))
    - ret = ilk_pipe_crc_ctl_reg(&source, &val);
    - else
    - ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val);
    -
    - if (ret != 0)
    - goto out;
    -
    - /* none -> real source transition */
    - if (source) {
    - struct intel_pipe_crc_entry *entries;
    -
    - DRM_DEBUG_DRIVER("collecting CRCs for pipe %c, %s\n",
    - pipe_name(pipe), pipe_crc_source_name(source));
    -
    - entries = kcalloc(INTEL_PIPE_CRC_ENTRIES_NR,
    - sizeof(pipe_crc->entries[0]),
    - GFP_KERNEL);
    - if (!entries) {
    - ret = -ENOMEM;
    - goto out;
    - }
    -
    - /*
    - * When IPS gets enabled, the pipe CRC changes. Since IPS gets
    - * enabled and disabled dynamically based on package C states,
    - * user space can't make reliable use of the CRCs, so let's just
    - * completely disable it.
    - */
    - hsw_disable_ips(crtc);
    -
    - spin_lock_irq(&pipe_crc->lock);
    - kfree(pipe_crc->entries);
    - pipe_crc->entries = entries;
    - pipe_crc->head = 0;
    - pipe_crc->tail = 0;
    - spin_unlock_irq(&pipe_crc->lock);
    - }
    -
    - pipe_crc->source = source;
    -
    - I915_WRITE(PIPE_CRC_CTL(pipe), val);
    - POSTING_READ(PIPE_CRC_CTL(pipe));
    -
    - /* real source -> none transition */
    - if (source == INTEL_PIPE_CRC_SOURCE_NONE) {
    - struct intel_pipe_crc_entry *entries;
    - struct intel_crtc *crtc =
    - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
    -
    - DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n",
    - pipe_name(pipe));
    -
    - drm_modeset_lock(&crtc->base.mutex, NULL);
    - if (crtc->base.state->active)
    - intel_wait_for_vblank(dev, pipe);
    - drm_modeset_unlock(&crtc->base.mutex);
    -
    - spin_lock_irq(&pipe_crc->lock);
    - entries = pipe_crc->entries;
    - pipe_crc->entries = NULL;
    - pipe_crc->head = 0;
    - pipe_crc->tail = 0;
    - spin_unlock_irq(&pipe_crc->lock);
    -
    - kfree(entries);
    -
    - if (IS_G4X(dev))
    - g4x_undo_pipe_scramble_reset(dev, pipe);
    - else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
    - vlv_undo_pipe_scramble_reset(dev, pipe);
    - else if (IS_HASWELL(dev) && pipe == PIPE_A)
    - hsw_trans_edp_pipe_A_crc_wa(dev, false);
    -
    - hsw_enable_ips(crtc);
    - }
    -
    - ret = 0;
    -
    -out:
    - intel_display_power_put(dev_priv, power_domain);
    -
    - return ret;
    -}
    -
    -/*
    - * Parse pipe CRC command strings:
    - * command: wsp* object wsp+ name wsp+ source wsp*
    - * object: 'pipe'
    - * name: (A | B | C)
    - * source: (none | plane1 | plane2 | pf)
    - * wsp: (#0x20 | #0x9 | #0xA)+
    - *
    - * eg.:
    - * "pipe A plane1" -> Start CRC computations on plane1 of pipe A
    - * "pipe A none" -> Stop CRC
    - */
    -static int display_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;
    -}
    -
    -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];
    -
    - if (name < 'A' || name >= pipe_name(I915_MAX_PIPES))
    - return -EINVAL;
    -
    - *pipe = name - 'A';
    -
    - return 0;
    -}
    -
    -static int
    -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])) {
    - *s = i;
    - return 0;
    - }
    -
    - return -EINVAL;
    -}
    -
    -static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len)
    -{
    -#define N_WORDS 3
    - int n_words;
    - char *words[N_WORDS];
    - enum pipe pipe;
    - enum intel_pipe_crc_object object;
    - enum intel_pipe_crc_source source;
    -
    - 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 (display_crc_ctl_parse_object(words[0], &object) < 0) {
    - DRM_DEBUG_DRIVER("unknown object %s\n", words[0]);
    - return -EINVAL;
    - }
    -
    - 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 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;
    - 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 = display_crc_ctl_parse(dev, tmpbuf, len);
    -
    -out:
    - kfree(tmpbuf);
    - if (ret < 0)
    - return ret;
    -
    - *offp += len;
    - return len;
    -}
    -
    -static const struct file_operations i915_display_crc_ctl_fops = {
    - .owner = THIS_MODULE,
    - .open = display_crc_ctl_open,
    - .read = seq_read,
    - .llseek = seq_lseek,
    - .release = single_release,
    - .write = display_crc_ctl_write
    -};
    -
    static ssize_t i915_displayport_test_active_write(struct file *file,
    const char __user *ubuf,
    size_t len, loff_t *offp)
    @@ -5467,20 +4604,6 @@ static const struct i915_debugfs_files {
    {"i915_dp_test_active", &i915_displayport_test_active_fops}
    };

    -void intel_display_crc_init(struct drm_device *dev)
    -{
    - struct drm_i915_private *dev_priv = to_i915(dev);
    - enum pipe pipe;
    -
    - for_each_pipe(dev_priv, pipe) {
    - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
    -
    - pipe_crc->opened = false;
    - spin_lock_init(&pipe_crc->lock);
    - init_waitqueue_head(&pipe_crc->wq);
    - }
    -}
    -
    int i915_debugfs_register(struct drm_i915_private *dev_priv)
    {
    struct drm_minor *minor = dev_priv->drm.primary;
    @@ -5490,11 +4613,9 @@ int i915_debugfs_register(struct drm_i915_private *dev_priv)
    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;
    - }
    + ret = intel_pipe_crc_create(minor);
    + if (ret)
    + return ret;

    for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) {
    ret = i915_debugfs_create(minor->debugfs_root, minor,
    @@ -5520,12 +4641,7 @@ void i915_debugfs_unregister(struct drm_i915_private *dev_priv)
    drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops,
    1, minor);

    - for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) {
    - struct drm_info_list *info_list =
    - (struct drm_info_list *)&i915_pipe_crc_data[i];
    -
    - drm_debugfs_remove_files(info_list, 1, minor);
    - }
    + intel_pipe_crc_cleanup(minor);

    for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) {
    struct drm_info_list *info_list =
    diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
    index e6a24d2154d1..5d06cb2425a1 100644
    --- a/drivers/gpu/drm/i915/intel_drv.h
    +++ b/drivers/gpu/drm/i915/intel_drv.h
    @@ -1779,4 +1779,9 @@ int intel_color_check(struct drm_crtc *crtc, struct drm_crtc_state *state);
    void intel_color_set_csc(struct drm_crtc_state *crtc_state);
    void intel_color_load_luts(struct drm_crtc_state *crtc_state);

    +/* intel_pipe_crc.c */
    +int intel_pipe_crc_create(struct drm_minor *minor);
    +void intel_pipe_crc_cleanup(struct drm_minor *minor);
    +extern const struct file_operations i915_display_crc_ctl_fops;
    +
    #endif /* __INTEL_DRV_H__ */
    diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c
    new file mode 100644
    index 000000000000..4e6c28ce8ecf
    --- /dev/null
    +++ b/drivers/gpu/drm/i915/intel_pipe_crc.c
    @@ -0,0 +1,951 @@
    +/*
    + * Copyright © 2013 Intel Corporation
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice (including the next
    + * paragraph) shall be included in all copies or substantial portions of the
    + * Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    + * IN THE SOFTWARE.
    + *
    + * Author: Damien Lespiau <damien.lespiau@intel.com>
    + *
    + */
    +
    +#include <linux/seq_file.h>
    +#include <linux/circ_buf.h>
    +#include <linux/ctype.h>
    +#include <linux/debugfs.h>
    +#include "intel_drv.h"
    +
    +struct pipe_crc_info {
    + const char *name;
    + struct drm_device *dev;
    + enum pipe pipe;
    +};
    +
    +/* 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_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 (info->pipe >= INTEL_INFO(info->dev)->num_pipes)
    + return -ENODEV;
    +
    + spin_lock_irq(&pipe_crc->lock);
    +
    + if (pipe_crc->opened) {
    + spin_unlock_irq(&pipe_crc->lock);
    + return -EBUSY; /* already open */
    + }
    +
    + pipe_crc->opened = true;
    + filep->private_data = inode->i_private;
    +
    + spin_unlock_irq(&pipe_crc->lock);
    +
    + 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];
    +
    + spin_lock_irq(&pipe_crc->lock);
    + pipe_crc->opened = false;
    + spin_unlock_irq(&pipe_crc->lock);
    +
    + 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)
    +{
    + assert_spin_locked(&pipe_crc->lock);
    + return CIRC_CNT(pipe_crc->head, pipe_crc->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 n_entries;
    + 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 */
    + spin_lock_irq(&pipe_crc->lock);
    + while (pipe_crc_data_count(pipe_crc) == 0) {
    + int ret;
    +
    + if (filep->f_flags & O_NONBLOCK) {
    + spin_unlock_irq(&pipe_crc->lock);
    + return -EAGAIN;
    + }
    +
    + ret = wait_event_interruptible_lock_irq(pipe_crc->wq,
    + pipe_crc_data_count(pipe_crc), pipe_crc->lock);
    + if (ret) {
    + spin_unlock_irq(&pipe_crc->lock);
    + return ret;
    + }
    + }
    +
    + /* We now have one or more entries to read */
    + n_entries = count / PIPE_CRC_LINE_LEN;
    +
    + bytes_read = 0;
    + while (n_entries > 0) {
    + struct intel_pipe_crc_entry *entry =
    + &pipe_crc->entries[pipe_crc->tail];
    + int ret;
    +
    + if (CIRC_CNT(pipe_crc->head, pipe_crc->tail,
    + INTEL_PIPE_CRC_ENTRIES_NR) < 1)
    + break;
    +
    + BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR);
    + pipe_crc->tail = (pipe_crc->tail + 1) &
    + (INTEL_PIPE_CRC_ENTRIES_NR - 1);
    +
    + 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]);
    +
    + spin_unlock_irq(&pipe_crc->lock);
    +
    + ret = copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN);
    + if (ret == PIPE_CRC_LINE_LEN)
    + return -EFAULT;
    +
    + user_buf += PIPE_CRC_LINE_LEN;
    + n_entries--;
    +
    + spin_lock_irq(&pipe_crc->lock);
    + }
    +
    + spin_unlock_irq(&pipe_crc->lock);
    +
    + return bytes_read;
    +}
    +
    +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 (!ent)
    + return -ENOMEM;
    +
    + return drm_add_fake_info_node(minor, ent, info);
    +}
    +
    +static const char * const pipe_crc_sources[] = {
    + "none",
    + "plane1",
    + "plane2",
    + "pf",
    + "pipe",
    + "TV",
    + "DP-B",
    + "DP-C",
    + "DP-D",
    + "auto",
    +};
    +
    +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 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;
    + 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 display_crc_ctl_open(struct inode *inode, struct file *file)
    +{
    + struct drm_device *dev = inode->i_private;
    +
    + return single_open(file, display_crc_ctl_show, dev);
    +}
    +
    +static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
    + uint32_t *val)
    +{
    + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
    + *source = INTEL_PIPE_CRC_SOURCE_PIPE;
    +
    + switch (*source) {
    + case INTEL_PIPE_CRC_SOURCE_PIPE:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_INCLUDE_BORDER_I8XX;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_NONE:
    + *val = 0;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
    + enum intel_pipe_crc_source *source)
    +{
    + struct intel_encoder *encoder;
    + struct intel_crtc *crtc;
    + struct intel_digital_port *dig_port;
    + int ret = 0;
    +
    + *source = INTEL_PIPE_CRC_SOURCE_PIPE;
    +
    + drm_modeset_lock_all(dev);
    + for_each_intel_encoder(dev, encoder) {
    + if (!encoder->base.crtc)
    + continue;
    +
    + crtc = to_intel_crtc(encoder->base.crtc);
    +
    + if (crtc->pipe != pipe)
    + continue;
    +
    + switch (encoder->type) {
    + case INTEL_OUTPUT_TVOUT:
    + *source = INTEL_PIPE_CRC_SOURCE_TV;
    + break;
    + case INTEL_OUTPUT_DISPLAYPORT:
    + case INTEL_OUTPUT_EDP:
    + dig_port = enc_to_dig_port(&encoder->base);
    + switch (dig_port->port) {
    + case PORT_B:
    + *source = INTEL_PIPE_CRC_SOURCE_DP_B;
    + break;
    + case PORT_C:
    + *source = INTEL_PIPE_CRC_SOURCE_DP_C;
    + break;
    + case PORT_D:
    + *source = INTEL_PIPE_CRC_SOURCE_DP_D;
    + break;
    + default:
    + WARN(1, "nonexisting DP port %c\n",
    + port_name(dig_port->port));
    + break;
    + }
    + break;
    + default:
    + break;
    + }
    + }
    + drm_modeset_unlock_all(dev);
    +
    + return ret;
    +}
    +
    +static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
    + enum pipe pipe,
    + enum intel_pipe_crc_source *source,
    + uint32_t *val)
    +{
    + struct drm_i915_private *dev_priv = dev->dev_private;
    + bool need_stable_symbols = false;
    + int ret;
    +
    + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
    + ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
    + if (ret)
    + return ret;
    + }
    +
    + switch (*source) {
    + case INTEL_PIPE_CRC_SOURCE_PIPE:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_VLV;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_DP_B:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_VLV;
    + need_stable_symbols = true;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_DP_C:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_VLV;
    + need_stable_symbols = true;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_DP_D:
    + if (!IS_CHERRYVIEW(dev))
    + return -EINVAL;
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_VLV;
    + need_stable_symbols = true;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_NONE:
    + *val = 0;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + /*
    + * When the pipe CRC tap point is after the transcoders we need
    + * to tweak symbol-level features to produce a deterministic series of
    + * symbols for a given frame. We need to reset those features only once
    + * a frame (instead of every nth symbol):
    + * - DC-balance: used to ensure a better clock recovery from the data
    + * link (SDVO)
    + * - DisplayPort scrambling: used for EMI reduction
    + */
    + if (need_stable_symbols) {
    + uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    +
    + tmp |= DC_BALANCE_RESET_VLV;
    + switch (pipe) {
    + case PIPE_A:
    + tmp |= PIPE_A_SCRAMBLE_RESET;
    + break;
    + case PIPE_B:
    + tmp |= PIPE_B_SCRAMBLE_RESET;
    + break;
    + case PIPE_C:
    + tmp |= PIPE_C_SCRAMBLE_RESET;
    + break;
    + default:
    + return -EINVAL;
    + }
    + I915_WRITE(PORT_DFT2_G4X, tmp);
    + }
    +
    + return 0;
    +}
    +
    +static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
    + enum pipe pipe,
    + enum intel_pipe_crc_source *source,
    + uint32_t *val)
    +{
    + struct drm_i915_private *dev_priv = dev->dev_private;
    + bool need_stable_symbols = false;
    + int ret;
    +
    + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
    + ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
    + if (ret)
    + return ret;
    + }
    +
    + switch (*source) {
    + case INTEL_PIPE_CRC_SOURCE_PIPE:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_TV:
    + if (!SUPPORTS_TV(dev))
    + return -EINVAL;
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_DP_B:
    + if (!IS_G4X(dev))
    + return -EINVAL;
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X;
    + need_stable_symbols = true;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_DP_C:
    + if (!IS_G4X(dev))
    + return -EINVAL;
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X;
    + need_stable_symbols = true;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_DP_D:
    + if (!IS_G4X(dev))
    + return -EINVAL;
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X;
    + need_stable_symbols = true;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_NONE:
    + *val = 0;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + /*
    + * When the pipe CRC tap point is after the transcoders we need
    + * to tweak symbol-level features to produce a deterministic series of
    + * symbols for a given frame. We need to reset those features only once
    + * a frame (instead of every nth symbol):
    + * - DC-balance: used to ensure a better clock recovery from the data
    + * link (SDVO)
    + * - DisplayPort scrambling: used for EMI reduction
    + */
    + if (need_stable_symbols) {
    + uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    +
    + WARN_ON(!IS_G4X(dev));
    +
    + I915_WRITE(PORT_DFT_I9XX,
    + I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET);
    +
    + if (pipe == PIPE_A)
    + tmp |= PIPE_A_SCRAMBLE_RESET;
    + else
    + tmp |= PIPE_B_SCRAMBLE_RESET;
    +
    + I915_WRITE(PORT_DFT2_G4X, tmp);
    + }
    +
    + return 0;
    +}
    +
    +static void vlv_undo_pipe_scramble_reset(struct drm_device *dev,
    + enum pipe pipe)
    +{
    + struct drm_i915_private *dev_priv = dev->dev_private;
    + uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    +
    + switch (pipe) {
    + case PIPE_A:
    + tmp &= ~PIPE_A_SCRAMBLE_RESET;
    + break;
    + case PIPE_B:
    + tmp &= ~PIPE_B_SCRAMBLE_RESET;
    + break;
    + case PIPE_C:
    + tmp &= ~PIPE_C_SCRAMBLE_RESET;
    + break;
    + default:
    + return;
    + }
    + if (!(tmp & PIPE_SCRAMBLE_RESET_MASK))
    + tmp &= ~DC_BALANCE_RESET_VLV;
    + I915_WRITE(PORT_DFT2_G4X, tmp);
    +
    +}
    +
    +static void g4x_undo_pipe_scramble_reset(struct drm_device *dev,
    + enum pipe pipe)
    +{
    + struct drm_i915_private *dev_priv = dev->dev_private;
    + uint32_t tmp = I915_READ(PORT_DFT2_G4X);
    +
    + if (pipe == PIPE_A)
    + tmp &= ~PIPE_A_SCRAMBLE_RESET;
    + else
    + tmp &= ~PIPE_B_SCRAMBLE_RESET;
    + I915_WRITE(PORT_DFT2_G4X, tmp);
    +
    + if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) {
    + I915_WRITE(PORT_DFT_I9XX,
    + I915_READ(PORT_DFT_I9XX) & ~DC_BALANCE_RESET);
    + }
    +}
    +
    +static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
    + uint32_t *val)
    +{
    + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
    + *source = INTEL_PIPE_CRC_SOURCE_PIPE;
    +
    + switch (*source) {
    + case INTEL_PIPE_CRC_SOURCE_PLANE1:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_ILK;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_PLANE2:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_ILK;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_PIPE:
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_ILK;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_NONE:
    + *val = 0;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev, bool enable)
    +{
    + struct drm_i915_private *dev_priv = dev->dev_private;
    + struct intel_crtc *crtc =
    + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]);
    + struct intel_crtc_state *pipe_config;
    + struct drm_atomic_state *state;
    + int ret = 0;
    +
    + drm_modeset_lock_all(dev);
    + state = drm_atomic_state_alloc(dev);
    + if (!state) {
    + ret = -ENOMEM;
    + goto out;
    + }
    +
    + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(&crtc->base);
    + pipe_config = intel_atomic_get_crtc_state(state, crtc);
    + if (IS_ERR(pipe_config)) {
    + ret = PTR_ERR(pipe_config);
    + goto out;
    + }
    +
    + pipe_config->pch_pfit.force_thru = enable;
    + if (pipe_config->cpu_transcoder == TRANSCODER_EDP &&
    + pipe_config->pch_pfit.enabled != enable)
    + pipe_config->base.connectors_changed = true;
    +
    + ret = drm_atomic_commit(state);
    +out:
    + drm_modeset_unlock_all(dev);
    + WARN(ret, "Toggling workaround to %i returns %i\n", enable, ret);
    + if (ret)
    + drm_atomic_state_free(state);
    +}
    +
    +static int ivb_pipe_crc_ctl_reg(struct drm_device *dev,
    + enum pipe pipe,
    + enum intel_pipe_crc_source *source,
    + uint32_t *val)
    +{
    + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
    + *source = INTEL_PIPE_CRC_SOURCE_PF;
    +
    + 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:
    + if (IS_HASWELL(dev) && pipe == PIPE_A)
    + hsw_trans_edp_pipe_A_crc_wa(dev, true);
    +
    + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB;
    + break;
    + case INTEL_PIPE_CRC_SOURCE_NONE:
    + *val = 0;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +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];
    + struct intel_crtc *crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev,
    + pipe));
    + enum intel_display_power_domain power_domain;
    + u32 val = 0; /* shut up gcc */
    + int ret;
    +
    + if (pipe_crc->source == source)
    + return 0;
    +
    + /* forbid changing the source without going back to 'none' */
    + if (pipe_crc->source && source)
    + return -EINVAL;
    +
    + power_domain = POWER_DOMAIN_PIPE(pipe);
    + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) {
    + DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n");
    + return -EIO;
    + }
    +
    + if (IS_GEN2(dev))
    + ret = i8xx_pipe_crc_ctl_reg(&source, &val);
    + else if (INTEL_INFO(dev)->gen < 5)
    + ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val);
    + else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
    + ret = vlv_pipe_crc_ctl_reg(dev, pipe, &source, &val);
    + else if (IS_GEN5(dev) || IS_GEN6(dev))
    + ret = ilk_pipe_crc_ctl_reg(&source, &val);
    + else
    + ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val);
    +
    + if (ret != 0)
    + goto out;
    +
    + /* none -> real source transition */
    + if (source) {
    + struct intel_pipe_crc_entry *entries;
    +
    + DRM_DEBUG_DRIVER("collecting CRCs for pipe %c, %s\n",
    + pipe_name(pipe), pipe_crc_source_name(source));
    +
    + entries = kcalloc(INTEL_PIPE_CRC_ENTRIES_NR,
    + sizeof(pipe_crc->entries[0]),
    + GFP_KERNEL);
    + if (!entries) {
    + ret = -ENOMEM;
    + goto out;
    + }
    +
    + /*
    + * When IPS gets enabled, the pipe CRC changes. Since IPS gets
    + * enabled and disabled dynamically based on package C states,
    + * user space can't make reliable use of the CRCs, so let's just
    + * completely disable it.
    + */
    + hsw_disable_ips(crtc);
    +
    + spin_lock_irq(&pipe_crc->lock);
    + kfree(pipe_crc->entries);
    + pipe_crc->entries = entries;
    + pipe_crc->head = 0;
    + pipe_crc->tail = 0;
    + spin_unlock_irq(&pipe_crc->lock);
    + }
    +
    + pipe_crc->source = source;
    +
    + I915_WRITE(PIPE_CRC_CTL(pipe), val);
    + POSTING_READ(PIPE_CRC_CTL(pipe));
    +
    + /* real source -> none transition */
    + if (source == INTEL_PIPE_CRC_SOURCE_NONE) {
    + struct intel_pipe_crc_entry *entries;
    + struct intel_crtc *crtc =
    + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
    +
    + DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n",
    + pipe_name(pipe));
    +
    + drm_modeset_lock(&crtc->base.mutex, NULL);
    + if (crtc->base.state->active)
    + intel_wait_for_vblank(dev, pipe);
    + drm_modeset_unlock(&crtc->base.mutex);
    +
    + spin_lock_irq(&pipe_crc->lock);
    + entries = pipe_crc->entries;
    + pipe_crc->entries = NULL;
    + pipe_crc->head = 0;
    + pipe_crc->tail = 0;
    + spin_unlock_irq(&pipe_crc->lock);
    +
    + kfree(entries);
    +
    + if (IS_G4X(dev))
    + g4x_undo_pipe_scramble_reset(dev, pipe);
    + else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
    + vlv_undo_pipe_scramble_reset(dev, pipe);
    + else if (IS_HASWELL(dev) && pipe == PIPE_A)
    + hsw_trans_edp_pipe_A_crc_wa(dev, false);
    +
    + hsw_enable_ips(crtc);
    + }
    +
    + ret = 0;
    +
    +out:
    + intel_display_power_put(dev_priv, power_domain);
    +
    + return ret;
    +}
    +
    +/*
    + * Parse pipe CRC command strings:
    + * command: wsp* object wsp+ name wsp+ source wsp*
    + * object: 'pipe'
    + * name: (A | B | C)
    + * source: (none | plane1 | plane2 | pf)
    + * wsp: (#0x20 | #0x9 | #0xA)+
    + *
    + * eg.:
    + * "pipe A plane1" -> Start CRC computations on plane1 of pipe A
    + * "pipe A none" -> Stop CRC
    + */
    +static int display_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;
    +}
    +
    +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];
    +
    + if (name < 'A' || name >= pipe_name(I915_MAX_PIPES))
    + return -EINVAL;
    +
    + *pipe = name - 'A';
    +
    + return 0;
    +}
    +
    +static int
    +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])) {
    + *s = i;
    + return 0;
    + }
    +
    + return -EINVAL;
    +}
    +
    +static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len)
    +{
    +#define N_WORDS 3
    + int n_words;
    + char *words[N_WORDS];
    + enum pipe pipe;
    + enum intel_pipe_crc_object object;
    + enum intel_pipe_crc_source source;
    +
    + 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 (display_crc_ctl_parse_object(words[0], &object) < 0) {
    + DRM_DEBUG_DRIVER("unknown object %s\n", words[0]);
    + return -EINVAL;
    + }
    +
    + 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 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;
    + 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 = display_crc_ctl_parse(dev, tmpbuf, len);
    +
    +out:
    + kfree(tmpbuf);
    + if (ret < 0)
    + return ret;
    +
    + *offp += len;
    + return len;
    +}
    +
    +const struct file_operations i915_display_crc_ctl_fops = {
    + .owner = THIS_MODULE,
    + .open = display_crc_ctl_open,
    + .read = seq_read,
    + .llseek = seq_lseek,
    + .release = single_release,
    + .write = display_crc_ctl_write
    +};
    +
    +void intel_display_crc_init(struct drm_device *dev)
    +{
    + struct drm_i915_private *dev_priv = dev->dev_private;
    + enum pipe pipe;
    +
    + for_each_pipe(dev_priv, pipe) {
    + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
    +
    + pipe_crc->opened = false;
    + spin_lock_init(&pipe_crc->lock);
    + init_waitqueue_head(&pipe_crc->wq);
    + }
    +}
    +
    +int intel_pipe_crc_create(struct drm_minor *minor)
    +{
    + int ret, i;
    +
    + 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;
    + }
    +
    + return 0;
    +}
    +
    +void intel_pipe_crc_cleanup(struct drm_minor *minor)
    +{
    + int i;
    +
    + for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) {
    + struct drm_info_list *info_list =
    + (struct drm_info_list *)&i915_pipe_crc_data[i];
    +
    + drm_debugfs_remove_files(info_list, 1, minor);
    + }
    +}
    --
    2.5.5
    \
     
     \ /
      Last update: 2016-07-22 17:01    [W:6.893 / U:0.060 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site