diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_debugfs.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_debugfs.c | 944 |
1 files changed, 806 insertions, 138 deletions
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e8b18e542da4..82bbe3f2a7e1 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -96,7 +96,7 @@ static int i915_capabilities(struct seq_file *m, void *data) static const char *get_pin_flag(struct drm_i915_gem_object *obj) { - if (i915_gem_obj_is_pinned(obj)) + if (obj->pin_display) return "p"; else return " "; @@ -120,18 +120,25 @@ static inline const char *get_global_flag(struct drm_i915_gem_object *obj) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct intel_engine_cs *ring; struct i915_vma *vma; int pin_count = 0; + int i; - seq_printf(m, "%pK: %s%s%s %8zdKiB %02x %02x %x %x %x%s%s%s", + seq_printf(m, "%pK: %s%s%s%s %8zdKiB %02x %02x [ ", &obj->base, + obj->active ? "*" : " ", get_pin_flag(obj), get_tiling_flag(obj), get_global_flag(obj), obj->base.size / 1024, obj->base.read_domains, - obj->base.write_domain, - i915_gem_request_get_seqno(obj->last_read_req), + obj->base.write_domain); + for_each_ring(ring, dev_priv, i) + seq_printf(m, "%x ", + i915_gem_request_get_seqno(obj->last_read_req[i])); + seq_printf(m, "] %x %x%s%s%s", i915_gem_request_get_seqno(obj->last_write_req), i915_gem_request_get_seqno(obj->last_fenced_req), i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level), @@ -139,10 +146,11 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) seq_printf(m, " (name: %d)", obj->base.name); - list_for_each_entry(vma, &obj->vma_list, vma_link) + list_for_each_entry(vma, &obj->vma_list, vma_link) { if (vma->pin_count > 0) pin_count++; - seq_printf(m, " (pinned x %d)", pin_count); + } + seq_printf(m, " (pinned x %d)", pin_count); if (obj->pin_display) seq_printf(m, " (display)"); if (obj->fence_reg != I915_FENCE_REG_NONE) @@ -158,18 +166,18 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) } if (obj->stolen) seq_printf(m, " (stolen: %08llx)", obj->stolen->start); - if (obj->pin_mappable || obj->fault_mappable) { + if (obj->pin_display || obj->fault_mappable) { char s[3], *t = s; - if (obj->pin_mappable) + if (obj->pin_display) *t++ = 'p'; if (obj->fault_mappable) *t++ = 'f'; *t = '\0'; seq_printf(m, " (%s mappable)", s); } - if (obj->last_read_req != NULL) + if (obj->last_write_req != NULL) seq_printf(m, " (%s)", - i915_gem_request_get_ring(obj->last_read_req)->name); + i915_gem_request_get_ring(obj->last_write_req)->name); if (obj->frontbuffer_bits) seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits); } @@ -360,31 +368,39 @@ static int per_file_stats(int id, void *ptr, void *data) return 0; } -#define print_file_stats(m, name, stats) \ - seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu shared, %zu unbound)\n", \ - name, \ - stats.count, \ - stats.total, \ - stats.active, \ - stats.inactive, \ - stats.global, \ - stats.shared, \ - stats.unbound) +#define print_file_stats(m, name, stats) do { \ + if (stats.count) \ + seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu shared, %zu unbound)\n", \ + name, \ + stats.count, \ + stats.total, \ + stats.active, \ + stats.inactive, \ + stats.global, \ + stats.shared, \ + stats.unbound); \ +} while (0) static void print_batch_pool_stats(struct seq_file *m, struct drm_i915_private *dev_priv) { struct drm_i915_gem_object *obj; struct file_stats stats; + struct intel_engine_cs *ring; + int i, j; memset(&stats, 0, sizeof(stats)); - list_for_each_entry(obj, - &dev_priv->mm.batch_pool.cache_list, - batch_pool_list) - per_file_stats(0, obj, &stats); + for_each_ring(ring, dev_priv, i) { + for (j = 0; j < ARRAY_SIZE(ring->batch_pool.cache_list); j++) { + list_for_each_entry(obj, + &ring->batch_pool.cache_list[j], + batch_pool_link) + per_file_stats(0, obj, &stats); + } + } - print_file_stats(m, "batch pool", stats); + print_file_stats(m, "[k]batch pool", stats); } #define count_vmas(list, member) do { \ @@ -448,7 +464,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) size += i915_gem_obj_ggtt_size(obj); ++count; } - if (obj->pin_mappable) { + if (obj->pin_display) { mappable_size += i915_gem_obj_ggtt_size(obj); ++mappable_count; } @@ -470,8 +486,6 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_putc(m, '\n'); print_batch_pool_stats(m, dev_priv); - - seq_putc(m, '\n'); list_for_each_entry_reverse(file, &dev->filelist, lhead) { struct file_stats stats; struct task_struct *task; @@ -580,7 +594,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "Flip queued on frame %d, (was ready on frame %d), now %d\n", work->flip_queued_vblank, work->flip_ready_vblank, - drm_vblank_count(dev, crtc->pipe)); + drm_crtc_vblank_count(&crtc->base)); if (work->enable_stall_check) seq_puts(m, "Stall check enabled, "); else @@ -612,24 +626,39 @@ static int i915_gem_batch_pool_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; - int count = 0; - int ret; + struct intel_engine_cs *ring; + int total = 0; + int ret, i, j; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; - seq_puts(m, "cache:\n"); - list_for_each_entry(obj, - &dev_priv->mm.batch_pool.cache_list, - batch_pool_list) { - seq_puts(m, " "); - describe_obj(m, obj); - seq_putc(m, '\n'); - count++; + for_each_ring(ring, dev_priv, i) { + for (j = 0; j < ARRAY_SIZE(ring->batch_pool.cache_list); j++) { + int count; + + count = 0; + list_for_each_entry(obj, + &ring->batch_pool.cache_list[j], + batch_pool_link) + count++; + seq_printf(m, "%s cache[%d]: %d objects\n", + ring->name, j, count); + + list_for_each_entry(obj, + &ring->batch_pool.cache_list[j], + batch_pool_link) { + seq_puts(m, " "); + describe_obj(m, obj); + seq_putc(m, '\n'); + } + + total += count; + } } - seq_printf(m, "total: %d\n", count); + seq_printf(m, "total: %d\n", total); mutex_unlock(&dev->struct_mutex); @@ -642,31 +671,44 @@ static int i915_gem_request_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; - struct drm_i915_gem_request *gem_request; - int ret, count, i; + struct drm_i915_gem_request *req; + int ret, any, i; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; - count = 0; + any = 0; for_each_ring(ring, dev_priv, i) { - if (list_empty(&ring->request_list)) + int count; + + count = 0; + list_for_each_entry(req, &ring->request_list, list) + count++; + if (count == 0) continue; - seq_printf(m, "%s requests:\n", ring->name); - list_for_each_entry(gem_request, - &ring->request_list, - list) { - seq_printf(m, " %x @ %d\n", - gem_request->seqno, - (int) (jiffies - gem_request->emitted_jiffies)); + seq_printf(m, "%s requests: %d\n", ring->name, count); + list_for_each_entry(req, &ring->request_list, list) { + struct task_struct *task; + + rcu_read_lock(); + task = NULL; + if (req->pid) + task = pid_task(req->pid, PIDTYPE_PID); + seq_printf(m, " %x @ %d: %s [%d]\n", + req->seqno, + (int) (jiffies - req->emitted_jiffies), + task ? task->comm : "<unknown>", + task ? task->pid : -1); + rcu_read_unlock(); } - count++; + + any++; } mutex_unlock(&dev->struct_mutex); - if (count == 0) + if (any == 0) seq_puts(m, "No requests\n"); return 0; @@ -1089,7 +1131,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "Current P-state: %d\n", (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT); } else if (IS_GEN6(dev) || (IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) || - IS_BROADWELL(dev)) { + IS_BROADWELL(dev) || IS_GEN9(dev)) { u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); @@ -1108,11 +1150,15 @@ static int i915_frequency_info(struct seq_file *m, void *unused) intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); reqf = I915_READ(GEN6_RPNSWREQ); - reqf &= ~GEN6_TURBO_DISABLE; - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) - reqf >>= 24; - else - reqf >>= 25; + if (IS_GEN9(dev)) + reqf >>= 23; + else { + reqf &= ~GEN6_TURBO_DISABLE; + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + reqf >>= 24; + else + reqf >>= 25; + } reqf = intel_gpu_freq(dev_priv, reqf); rpmodectl = I915_READ(GEN6_RP_CONTROL); @@ -1126,7 +1172,9 @@ static int i915_frequency_info(struct seq_file *m, void *unused) rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI); rpcurdown = I915_READ(GEN6_RP_CUR_DOWN); rpprevdown = I915_READ(GEN6_RP_PREV_DOWN); - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + if (IS_GEN9(dev)) + cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT; + else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; else cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; @@ -1152,7 +1200,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) pm_ier, pm_imr, pm_isr, pm_iir, pm_mask); seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status); seq_printf(m, "Render p-state ratio: %d\n", - (gt_perf_status & 0xff00) >> 8); + (gt_perf_status & (IS_GEN9(dev) ? 0x1ff00 : 0xff00)) >> 8); seq_printf(m, "Render p-state VID: %d\n", gt_perf_status & 0xff); seq_printf(m, "Render p-state limit: %d\n", @@ -1169,27 +1217,47 @@ static int i915_frequency_info(struct seq_file *m, void *unused) GEN6_CURBSYTAVG_MASK); seq_printf(m, "RP PREV UP: %dus\n", rpprevup & GEN6_CURBSYTAVG_MASK); + seq_printf(m, "Up threshold: %d%%\n", + dev_priv->rps.up_threshold); + seq_printf(m, "RP CUR DOWN EI: %dus\n", rpdownei & GEN6_CURIAVG_MASK); seq_printf(m, "RP CUR DOWN: %dus\n", rpcurdown & GEN6_CURBSYTAVG_MASK); seq_printf(m, "RP PREV DOWN: %dus\n", rpprevdown & GEN6_CURBSYTAVG_MASK); + seq_printf(m, "Down threshold: %d%%\n", + dev_priv->rps.down_threshold); max_freq = (rp_state_cap & 0xff0000) >> 16; + max_freq *= (IS_SKYLAKE(dev) ? GEN9_FREQ_SCALER : 1); seq_printf(m, "Lowest (RPN) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); max_freq = (rp_state_cap & 0xff00) >> 8; + max_freq *= (IS_SKYLAKE(dev) ? GEN9_FREQ_SCALER : 1); seq_printf(m, "Nominal (RP1) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); max_freq = rp_state_cap & 0xff; + max_freq *= (IS_SKYLAKE(dev) ? GEN9_FREQ_SCALER : 1); seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); - seq_printf(m, "Max overclocked frequency: %dMHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.max_freq)); + + seq_printf(m, "Current freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq)); + seq_printf(m, "Actual freq: %d MHz\n", cagf); + seq_printf(m, "Idle freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq)); + seq_printf(m, "Min freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.min_freq)); + seq_printf(m, "Max freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.max_freq)); + seq_printf(m, + "efficient (RPe) frequency: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq)); } else if (IS_VALLEYVIEW(dev)) { u32 freq_sts; @@ -1198,18 +1266,24 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts); seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq); + seq_printf(m, "actual GPU freq: %d MHz\n", + intel_gpu_freq(dev_priv, (freq_sts >> 8) & 0xff)); + + seq_printf(m, "current GPU freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq)); + seq_printf(m, "max GPU freq: %d MHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.max_freq)); seq_printf(m, "min GPU freq: %d MHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.min_freq)); + seq_printf(m, "idle GPU freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq)); + seq_printf(m, "efficient (RPe) frequency: %d MHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq)); - - seq_printf(m, "current GPU freq: %d MHz\n", - intel_gpu_freq(dev_priv, (freq_sts >> 8) & 0xff)); mutex_unlock(&dev_priv->rps.hw_lock); } else { seq_puts(m, "no P-state info available\n"); @@ -1651,12 +1725,15 @@ static int i915_sr_status(struct seq_file *m, void *unused) if (HAS_PCH_SPLIT(dev)) sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN; - else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev)) + else if (IS_CRESTLINE(dev) || IS_G4X(dev) || + IS_I945G(dev) || IS_I945GM(dev)) sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; else if (IS_I915GM(dev)) sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN; else if (IS_PINEVIEW(dev)) sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN; + else if (IS_VALLEYVIEW(dev)) + sr_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; intel_runtime_pm_put(dev_priv); @@ -1778,11 +1855,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) ifbdev = dev_priv->fbdev; fb = to_intel_framebuffer(ifbdev->helper.fb); - seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ", + seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, fb->base.bits_per_pixel, + fb->base.modifier[0], atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_putc(m, '\n'); @@ -1793,11 +1871,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) if (ifbdev && &fb->base == ifbdev->helper.fb) continue; - seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ", + seq_printf(m, "user size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, fb->base.bits_per_pixel, + fb->base.modifier[0], atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_putc(m, '\n'); @@ -1828,18 +1907,6 @@ static int i915_context_status(struct seq_file *m, void *unused) if (ret) return ret; - if (dev_priv->ips.pwrctx) { - seq_puts(m, "power context "); - describe_obj(m, dev_priv->ips.pwrctx); - seq_putc(m, '\n'); - } - - if (dev_priv->ips.renderctx) { - seq_puts(m, "render context "); - describe_obj(m, dev_priv->ips.renderctx); - seq_putc(m, '\n'); - } - list_for_each_entry(ctx, &dev_priv->context_list, link) { if (!i915.enable_execlists && ctx->legacy_hw_ctx.rcs_state == NULL) @@ -2147,8 +2214,6 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev) if (!ppgtt) return; - seq_printf(m, "Page directories: %d\n", ppgtt->num_pd_pages); - seq_printf(m, "Page tables: %d\n", ppgtt->num_pd_entries); for_each_ring(ring, dev_priv, unused) { seq_printf(m, "%s\n", ring->name); for (i = 0; i < 4; i++) { @@ -2183,7 +2248,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev) struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; seq_puts(m, "aliasing PPGTT:\n"); - seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset); + seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd.pd_offset); ppgtt->debug_dump(ppgtt, m); } @@ -2220,6 +2285,60 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) return 0; } +static int count_irq_waiters(struct drm_i915_private *i915) +{ + struct intel_engine_cs *ring; + int count = 0; + int i; + + for_each_ring(ring, i915, i) + count += ring->irq_refcount; + + return count; +} + +static int i915_rps_boost_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_file *file; + + seq_printf(m, "RPS enabled? %d\n", dev_priv->rps.enabled); + seq_printf(m, "GPU busy? %d\n", dev_priv->mm.busy); + seq_printf(m, "CPU waiting? %d\n", count_irq_waiters(dev_priv)); + seq_printf(m, "Frequency requested %d; min hard:%d, soft:%d; max soft:%d, hard:%d\n", + intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq), + intel_gpu_freq(dev_priv, dev_priv->rps.min_freq), + intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit), + intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit), + intel_gpu_freq(dev_priv, dev_priv->rps.max_freq)); + spin_lock(&dev_priv->rps.client_lock); + list_for_each_entry_reverse(file, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv = file->driver_priv; + struct task_struct *task; + + rcu_read_lock(); + task = pid_task(file->pid, PIDTYPE_PID); + seq_printf(m, "%s [%d]: %d boosts%s\n", + task ? task->comm : "<unknown>", + task ? task->pid : -1, + file_priv->rps.boosts, + list_empty(&file_priv->rps.link) ? "" : ", active"); + rcu_read_unlock(); + } + seq_printf(m, "Semaphore boosts: %d%s\n", + dev_priv->rps.semaphores.boosts, + list_empty(&dev_priv->rps.semaphores.link) ? "" : ", active"); + seq_printf(m, "MMIO flip boosts: %d%s\n", + dev_priv->rps.mmioflips.boosts, + list_empty(&dev_priv->rps.mmioflips.link) ? "" : ", active"); + seq_printf(m, "Kernel boosts: %d\n", dev_priv->rps.boosts); + spin_unlock(&dev_priv->rps.client_lock); + + return 0; +} + static int i915_llc(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; @@ -2243,6 +2362,11 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) enum pipe pipe; bool enabled = false; + if (!HAS_PSR(dev)) { + seq_puts(m, "PSR not supported\n"); + return 0; + } + intel_runtime_pm_get(dev_priv); mutex_lock(&dev_priv->psr.lock); @@ -2255,17 +2379,15 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "Re-enable work scheduled: %s\n", yesno(work_busy(&dev_priv->psr.work.work))); - if (HAS_PSR(dev)) { - if (HAS_DDI(dev)) - enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE; - else { - for_each_pipe(dev_priv, pipe) { - stat[pipe] = I915_READ(VLV_PSRSTAT(pipe)) & - VLV_EDP_PSR_CURR_STATE_MASK; - if ((stat[pipe] == VLV_EDP_PSR_ACTIVE_NORFB_UP) || - (stat[pipe] == VLV_EDP_PSR_ACTIVE_SF_UPDATE)) - enabled = true; - } + if (HAS_DDI(dev)) + enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE; + else { + for_each_pipe(dev_priv, pipe) { + stat[pipe] = I915_READ(VLV_PSRSTAT(pipe)) & + VLV_EDP_PSR_CURR_STATE_MASK; + if ((stat[pipe] == VLV_EDP_PSR_ACTIVE_NORFB_UP) || + (stat[pipe] == VLV_EDP_PSR_ACTIVE_SF_UPDATE)) + enabled = true; } } seq_printf(m, "HW Enabled & Active bit: %s", yesno(enabled)); @@ -2278,11 +2400,8 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) } seq_puts(m, "\n"); - seq_printf(m, "Link standby: %s\n", - yesno((bool)dev_priv->psr.link_standby)); - /* CHV PSR has no kind of performance counter */ - if (HAS_PSR(dev) && HAS_DDI(dev)) { + if (HAS_DDI(dev)) { psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) & EDP_PSR_PERF_CNT_MASK; @@ -2305,8 +2424,7 @@ static int i915_sink_crc(struct seq_file *m, void *data) u8 crc[6]; drm_modeset_lock_all(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, - base.head) { + for_each_intel_connector(dev, connector) { if (connector->base.dpms != DRM_MODE_DPMS_ON) continue; @@ -2674,7 +2792,8 @@ static int i915_display_info(struct seq_file *m, void *unused) active = cursor_position(dev, crtc->pipe, &x, &y); seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n", yesno(crtc->cursor_base), - x, y, crtc->cursor_width, crtc->cursor_height, + x, y, crtc->base.cursor->state->crtc_w, + crtc->base.cursor->state->crtc_h, crtc->cursor_addr, yesno(active)); } @@ -2850,7 +2969,7 @@ static int i915_ddb_info(struct seq_file *m, void *unused) for_each_pipe(dev_priv, pipe) { seq_printf(m, "Pipe %c\n", pipe_name(pipe)); - for_each_plane(pipe, plane) { + for_each_plane(dev_priv, pipe, plane) { entry = &ddb->plane[pipe][plane]; seq_printf(m, " Plane%-8d%8u%8u%8u\n", plane + 1, entry->start, entry->end, @@ -2867,6 +2986,115 @@ static int i915_ddb_info(struct seq_file *m, void *unused) return 0; } +static void drrs_status_per_crtc(struct seq_file *m, + struct drm_device *dev, struct intel_crtc *intel_crtc) +{ + struct intel_encoder *intel_encoder; + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_drrs *drrs = &dev_priv->drrs; + int vrefresh = 0; + + for_each_encoder_on_crtc(dev, &intel_crtc->base, intel_encoder) { + /* Encoder connected on this CRTC */ + switch (intel_encoder->type) { + case INTEL_OUTPUT_EDP: + seq_puts(m, "eDP:\n"); + break; + case INTEL_OUTPUT_DSI: + seq_puts(m, "DSI:\n"); + break; + case INTEL_OUTPUT_HDMI: + seq_puts(m, "HDMI:\n"); + break; + case INTEL_OUTPUT_DISPLAYPORT: + seq_puts(m, "DP:\n"); + break; + default: + seq_printf(m, "Other encoder (id=%d).\n", + intel_encoder->type); + return; + } + } + + if (dev_priv->vbt.drrs_type == STATIC_DRRS_SUPPORT) + seq_puts(m, "\tVBT: DRRS_type: Static"); + else if (dev_priv->vbt.drrs_type == SEAMLESS_DRRS_SUPPORT) + seq_puts(m, "\tVBT: DRRS_type: Seamless"); + else if (dev_priv->vbt.drrs_type == DRRS_NOT_SUPPORTED) + seq_puts(m, "\tVBT: DRRS_type: None"); + else + seq_puts(m, "\tVBT: DRRS_type: FIXME: Unrecognized Value"); + + seq_puts(m, "\n\n"); + + if (intel_crtc->config->has_drrs) { + struct intel_panel *panel; + + mutex_lock(&drrs->mutex); + /* DRRS Supported */ + seq_puts(m, "\tDRRS Supported: Yes\n"); + + /* disable_drrs() will make drrs->dp NULL */ + if (!drrs->dp) { + seq_puts(m, "Idleness DRRS: Disabled"); + mutex_unlock(&drrs->mutex); + return; + } + + panel = &drrs->dp->attached_connector->panel; + seq_printf(m, "\t\tBusy_frontbuffer_bits: 0x%X", + drrs->busy_frontbuffer_bits); + + seq_puts(m, "\n\t\t"); + if (drrs->refresh_rate_type == DRRS_HIGH_RR) { + seq_puts(m, "DRRS_State: DRRS_HIGH_RR\n"); + vrefresh = panel->fixed_mode->vrefresh; + } else if (drrs->refresh_rate_type == DRRS_LOW_RR) { + seq_puts(m, "DRRS_State: DRRS_LOW_RR\n"); + vrefresh = panel->downclock_mode->vrefresh; + } else { + seq_printf(m, "DRRS_State: Unknown(%d)\n", + drrs->refresh_rate_type); + mutex_unlock(&drrs->mutex); + return; + } + seq_printf(m, "\t\tVrefresh: %d", vrefresh); + + seq_puts(m, "\n\t\t"); + mutex_unlock(&drrs->mutex); + } else { + /* DRRS not supported. Print the VBT parameter*/ + seq_puts(m, "\tDRRS Supported : No"); + } + seq_puts(m, "\n"); +} + +static int i915_drrs_status(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct intel_crtc *intel_crtc; + int active_crtc_cnt = 0; + + for_each_intel_crtc(dev, intel_crtc) { + drm_modeset_lock(&intel_crtc->base.mutex, NULL); + + if (intel_crtc->active) { + active_crtc_cnt++; + seq_printf(m, "\nCRTC %d: ", active_crtc_cnt); + + drrs_status_per_crtc(m, dev, intel_crtc); + } + + drm_modeset_unlock(&intel_crtc->base.mutex); + } + + if (!active_crtc_cnt) + seq_puts(m, "No active crtc found\n"); + + return 0; +} + struct pipe_crc_info { const char *name; struct drm_device *dev; @@ -3408,8 +3636,7 @@ static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev) intel_display_power_get(dev_priv, POWER_DOMAIN_PIPE_PANEL_FITTER(PIPE_A)); - dev_priv->display.crtc_disable(&crtc->base); - dev_priv->display.crtc_enable(&crtc->base); + intel_crtc_reset(crtc); } drm_modeset_unlock_all(dev); } @@ -3430,8 +3657,7 @@ static void hsw_undo_trans_edp_pipe_A_crc_wa(struct drm_device *dev) if (crtc->config->pch_pfit.force_thru) { crtc->config->pch_pfit.force_thru = false; - dev_priv->display.crtc_disable(&crtc->base); - dev_priv->display.crtc_enable(&crtc->base); + intel_crtc_reset(crtc); intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_PANEL_FITTER(PIPE_A)); @@ -3748,6 +3974,212 @@ static const struct file_operations i915_display_crc_ctl_fops = { .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) +{ + char *input_buffer; + int status = 0; + struct seq_file *m; + struct drm_device *dev; + struct drm_connector *connector; + struct list_head *connector_list; + struct intel_dp *intel_dp; + int val = 0; + + m = file->private_data; + if (!m) { + status = -ENODEV; + return status; + } + dev = m->private; + + if (!dev) { + status = -ENODEV; + return status; + } + connector_list = &dev->mode_config.connector_list; + + if (len == 0) + return 0; + + input_buffer = kmalloc(len + 1, GFP_KERNEL); + if (!input_buffer) + return -ENOMEM; + + if (copy_from_user(input_buffer, ubuf, len)) { + status = -EFAULT; + goto out; + } + + input_buffer[len] = '\0'; + DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len); + + list_for_each_entry(connector, connector_list, head) { + + if (connector->connector_type != + DRM_MODE_CONNECTOR_DisplayPort) + continue; + + if (connector->connector_type == + DRM_MODE_CONNECTOR_DisplayPort && + connector->status == connector_status_connected && + connector->encoder != NULL) { + intel_dp = enc_to_intel_dp(connector->encoder); + status = kstrtoint(input_buffer, 10, &val); + if (status < 0) + goto out; + DRM_DEBUG_DRIVER("Got %d for test active\n", val); + /* To prevent erroneous activation of the compliance + * testing code, only accept an actual value of 1 here + */ + if (val == 1) + intel_dp->compliance_test_active = 1; + else + intel_dp->compliance_test_active = 0; + } + } +out: + kfree(input_buffer); + if (status < 0) + return status; + + *offp += len; + return len; +} + +static int i915_displayport_test_active_show(struct seq_file *m, void *data) +{ + struct drm_device *dev = m->private; + struct drm_connector *connector; + struct list_head *connector_list = &dev->mode_config.connector_list; + struct intel_dp *intel_dp; + + if (!dev) + return -ENODEV; + + list_for_each_entry(connector, connector_list, head) { + + if (connector->connector_type != + DRM_MODE_CONNECTOR_DisplayPort) + continue; + + if (connector->status == connector_status_connected && + connector->encoder != NULL) { + intel_dp = enc_to_intel_dp(connector->encoder); + if (intel_dp->compliance_test_active) + seq_puts(m, "1"); + else + seq_puts(m, "0"); + } else + seq_puts(m, "0"); + } + + return 0; +} + +static int i915_displayport_test_active_open(struct inode *inode, + struct file *file) +{ + struct drm_device *dev = inode->i_private; + + return single_open(file, i915_displayport_test_active_show, dev); +} + +static const struct file_operations i915_displayport_test_active_fops = { + .owner = THIS_MODULE, + .open = i915_displayport_test_active_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = i915_displayport_test_active_write +}; + +static int i915_displayport_test_data_show(struct seq_file *m, void *data) +{ + struct drm_device *dev = m->private; + struct drm_connector *connector; + struct list_head *connector_list = &dev->mode_config.connector_list; + struct intel_dp *intel_dp; + + if (!dev) + return -ENODEV; + + list_for_each_entry(connector, connector_list, head) { + + if (connector->connector_type != + DRM_MODE_CONNECTOR_DisplayPort) + continue; + + if (connector->status == connector_status_connected && + connector->encoder != NULL) { + intel_dp = enc_to_intel_dp(connector->encoder); + seq_printf(m, "%lx", intel_dp->compliance_test_data); + } else + seq_puts(m, "0"); + } + + return 0; +} +static int i915_displayport_test_data_open(struct inode *inode, + struct file *file) +{ + struct drm_device *dev = inode->i_private; + + return single_open(file, i915_displayport_test_data_show, dev); +} + +static const struct file_operations i915_displayport_test_data_fops = { + .owner = THIS_MODULE, + .open = i915_displayport_test_data_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static int i915_displayport_test_type_show(struct seq_file *m, void *data) +{ + struct drm_device *dev = m->private; + struct drm_connector *connector; + struct list_head *connector_list = &dev->mode_config.connector_list; + struct intel_dp *intel_dp; + + if (!dev) + return -ENODEV; + + list_for_each_entry(connector, connector_list, head) { + + if (connector->connector_type != + DRM_MODE_CONNECTOR_DisplayPort) + continue; + + if (connector->status == connector_status_connected && + connector->encoder != NULL) { + intel_dp = enc_to_intel_dp(connector->encoder); + seq_printf(m, "%02lx", intel_dp->compliance_test_type); + } else + seq_puts(m, "0"); + } + + return 0; +} + +static int i915_displayport_test_type_open(struct inode *inode, + struct file *file) +{ + struct drm_device *dev = inode->i_private; + + return single_open(file, i915_displayport_test_type_show, dev); +} + +static const struct file_operations i915_displayport_test_type_fops = { + .owner = THIS_MODULE, + .open = i915_displayport_test_type_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + static void wm_latency_show(struct seq_file *m, const uint16_t wm[8]) { struct drm_device *dev = m->private; @@ -4189,7 +4621,7 @@ i915_max_freq_set(void *data, u64 val) { struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - u32 rp_state_cap, hw_max, hw_min; + u32 hw_max, hw_min; int ret; if (INTEL_INFO(dev)->gen < 6) @@ -4206,18 +4638,10 @@ i915_max_freq_set(void *data, u64 val) /* * Turbo will still be enabled, but won't go above the set value. */ - if (IS_VALLEYVIEW(dev)) { - val = intel_freq_opcode(dev_priv, val); + val = intel_freq_opcode(dev_priv, val); - hw_max = dev_priv->rps.max_freq; - hw_min = dev_priv->rps.min_freq; - } else { - val = intel_freq_opcode(dev_priv, val); - - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.max_freq; - hw_min = (rp_state_cap >> 16) & 0xff; - } + hw_max = dev_priv->rps.max_freq; + hw_min = dev_priv->rps.min_freq; if (val < hw_min || val > hw_max || val < dev_priv->rps.min_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); @@ -4226,10 +4650,7 @@ i915_max_freq_set(void *data, u64 val) dev_priv->rps.max_freq_softlimit = val; - if (IS_VALLEYVIEW(dev)) - valleyview_set_rps(dev, val); - else - gen6_set_rps(dev, val); + intel_set_rps(dev, val); mutex_unlock(&dev_priv->rps.hw_lock); @@ -4267,7 +4688,7 @@ i915_min_freq_set(void *data, u64 val) { struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - u32 rp_state_cap, hw_max, hw_min; + u32 hw_max, hw_min; int ret; if (INTEL_INFO(dev)->gen < 6) @@ -4284,18 +4705,10 @@ i915_min_freq_set(void *data, u64 val) /* * Turbo will still be enabled, but won't go below the set value. */ - if (IS_VALLEYVIEW(dev)) { - val = intel_freq_opcode(dev_priv, val); + val = intel_freq_opcode(dev_priv, val); - hw_max = dev_priv->rps.max_freq; - hw_min = dev_priv->rps.min_freq; - } else { - val = intel_freq_opcode(dev_priv, val); - - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.max_freq; - hw_min = (rp_state_cap >> 16) & 0xff; - } + hw_max = dev_priv->rps.max_freq; + hw_min = dev_priv->rps.min_freq; if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); @@ -4304,10 +4717,7 @@ i915_min_freq_set(void *data, u64 val) dev_priv->rps.min_freq_softlimit = val; - if (IS_VALLEYVIEW(dev)) - valleyview_set_rps(dev, val); - else - gen6_set_rps(dev, val); + intel_set_rps(dev, val); mutex_unlock(&dev_priv->rps.hw_lock); @@ -4374,6 +4784,159 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops, i915_cache_sharing_get, i915_cache_sharing_set, "%llu\n"); +struct sseu_dev_status { + unsigned int slice_total; + unsigned int subslice_total; + unsigned int subslice_per_slice; + unsigned int eu_total; + unsigned int eu_per_subslice; +}; + +static void cherryview_sseu_device_status(struct drm_device *dev, + struct sseu_dev_status *stat) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + const int ss_max = 2; + int ss; + u32 sig1[ss_max], sig2[ss_max]; + + sig1[0] = I915_READ(CHV_POWER_SS0_SIG1); + sig1[1] = I915_READ(CHV_POWER_SS1_SIG1); + sig2[0] = I915_READ(CHV_POWER_SS0_SIG2); + sig2[1] = I915_READ(CHV_POWER_SS1_SIG2); + + for (ss = 0; ss < ss_max; ss++) { + unsigned int eu_cnt; + + if (sig1[ss] & CHV_SS_PG_ENABLE) + /* skip disabled subslice */ + continue; + + stat->slice_total = 1; + stat->subslice_per_slice++; + eu_cnt = ((sig1[ss] & CHV_EU08_PG_ENABLE) ? 0 : 2) + + ((sig1[ss] & CHV_EU19_PG_ENABLE) ? 0 : 2) + + ((sig1[ss] & CHV_EU210_PG_ENABLE) ? 0 : 2) + + ((sig2[ss] & CHV_EU311_PG_ENABLE) ? 0 : 2); + stat->eu_total += eu_cnt; + stat->eu_per_subslice = max(stat->eu_per_subslice, eu_cnt); + } + stat->subslice_total = stat->subslice_per_slice; +} + +static void gen9_sseu_device_status(struct drm_device *dev, + struct sseu_dev_status *stat) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int s_max = 3, ss_max = 4; + int s, ss; + u32 s_reg[s_max], eu_reg[2*s_max], eu_mask[2]; + + /* BXT has a single slice and at most 3 subslices. */ + if (IS_BROXTON(dev)) { + s_max = 1; + ss_max = 3; + } + + for (s = 0; s < s_max; s++) { + s_reg[s] = I915_READ(GEN9_SLICE_PGCTL_ACK(s)); + eu_reg[2*s] = I915_READ(GEN9_SS01_EU_PGCTL_ACK(s)); + eu_reg[2*s + 1] = I915_READ(GEN9_SS23_EU_PGCTL_ACK(s)); + } + + eu_mask[0] = GEN9_PGCTL_SSA_EU08_ACK | + GEN9_PGCTL_SSA_EU19_ACK | + GEN9_PGCTL_SSA_EU210_ACK | + GEN9_PGCTL_SSA_EU311_ACK; + eu_mask[1] = GEN9_PGCTL_SSB_EU08_ACK | + GEN9_PGCTL_SSB_EU19_ACK | + GEN9_PGCTL_SSB_EU210_ACK | + GEN9_PGCTL_SSB_EU311_ACK; + + for (s = 0; s < s_max; s++) { + unsigned int ss_cnt = 0; + + if ((s_reg[s] & GEN9_PGCTL_SLICE_ACK) == 0) + /* skip disabled slice */ + continue; + + stat->slice_total++; + + if (IS_SKYLAKE(dev)) + ss_cnt = INTEL_INFO(dev)->subslice_per_slice; + + for (ss = 0; ss < ss_max; ss++) { + unsigned int eu_cnt; + + if (IS_BROXTON(dev) && + !(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss)))) + /* skip disabled subslice */ + continue; + + if (IS_BROXTON(dev)) + ss_cnt++; + + eu_cnt = 2 * hweight32(eu_reg[2*s + ss/2] & + eu_mask[ss%2]); + stat->eu_total += eu_cnt; + stat->eu_per_subslice = max(stat->eu_per_subslice, + eu_cnt); + } + + stat->subslice_total += ss_cnt; + stat->subslice_per_slice = max(stat->subslice_per_slice, + ss_cnt); + } +} + +static int i915_sseu_status(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct sseu_dev_status stat; + + if ((INTEL_INFO(dev)->gen < 8) || IS_BROADWELL(dev)) + return -ENODEV; + + seq_puts(m, "SSEU Device Info\n"); + seq_printf(m, " Available Slice Total: %u\n", + INTEL_INFO(dev)->slice_total); + seq_printf(m, " Available Subslice Total: %u\n", + INTEL_INFO(dev)->subslice_total); + seq_printf(m, " Available Subslice Per Slice: %u\n", + INTEL_INFO(dev)->subslice_per_slice); + seq_printf(m, " Available EU Total: %u\n", + INTEL_INFO(dev)->eu_total); + seq_printf(m, " Available EU Per Subslice: %u\n", + INTEL_INFO(dev)->eu_per_subslice); + seq_printf(m, " Has Slice Power Gating: %s\n", + yesno(INTEL_INFO(dev)->has_slice_pg)); + seq_printf(m, " Has Subslice Power Gating: %s\n", + yesno(INTEL_INFO(dev)->has_subslice_pg)); + seq_printf(m, " Has EU Power Gating: %s\n", + yesno(INTEL_INFO(dev)->has_eu_pg)); + + seq_puts(m, "SSEU Device Status\n"); + memset(&stat, 0, sizeof(stat)); + if (IS_CHERRYVIEW(dev)) { + cherryview_sseu_device_status(dev, &stat); + } else if (INTEL_INFO(dev)->gen >= 9) { + gen9_sseu_device_status(dev, &stat); + } + seq_printf(m, " Enabled Slice Total: %u\n", + stat.slice_total); + seq_printf(m, " Enabled Subslice Total: %u\n", + stat.subslice_total); + seq_printf(m, " Enabled Subslice Per Slice: %u\n", + stat.subslice_per_slice); + seq_printf(m, " Enabled EU Total: %u\n", + stat.eu_total); + seq_printf(m, " Enabled EU Per Subslice: %u\n", + stat.eu_per_subslice); + + return 0; +} + static int i915_forcewake_open(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; @@ -4487,6 +5050,9 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_dp_mst_info", i915_dp_mst_info, 0}, {"i915_wa_registers", i915_wa_registers, 0}, {"i915_ddb_info", i915_ddb_info, 0}, + {"i915_sseu_status", i915_sseu_status, 0}, + {"i915_drrs_status", i915_drrs_status, 0}, + {"i915_rps_boost_info", i915_rps_boost_info, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) @@ -4509,6 +5075,9 @@ static const struct i915_debugfs_files { {"i915_spr_wm_latency", &i915_spr_wm_latency_fops}, {"i915_cur_wm_latency", &i915_cur_wm_latency_fops}, {"i915_fbc_false_color", &i915_fbc_fc_fops}, + {"i915_dp_test_data", &i915_displayport_test_data_fops}, + {"i915_dp_test_type", &i915_displayport_test_type_fops}, + {"i915_dp_test_active", &i915_displayport_test_active_fops} }; void intel_display_crc_init(struct drm_device *dev) @@ -4576,3 +5145,102 @@ void i915_debugfs_cleanup(struct drm_minor *minor) drm_debugfs_remove_files(info_list, 1, minor); } } + +struct dpcd_block { + /* DPCD dump start address. */ + unsigned int offset; + /* DPCD dump end address, inclusive. If unset, .size will be used. */ + unsigned int end; + /* DPCD dump size. Used if .end is unset. If unset, defaults to 1. */ + size_t size; + /* Only valid for eDP. */ + bool edp; +}; + +static const struct dpcd_block i915_dpcd_debug[] = { + { .offset = DP_DPCD_REV, .size = DP_RECEIVER_CAP_SIZE }, + { .offset = DP_PSR_SUPPORT, .end = DP_PSR_CAPS }, + { .offset = DP_DOWNSTREAM_PORT_0, .size = 16 }, + { .offset = DP_LINK_BW_SET, .end = DP_EDP_CONFIGURATION_SET }, + { .offset = DP_SINK_COUNT, .end = DP_ADJUST_REQUEST_LANE2_3 }, + { .offset = DP_SET_POWER }, + { .offset = DP_EDP_DPCD_REV }, + { .offset = DP_EDP_GENERAL_CAP_1, .end = DP_EDP_GENERAL_CAP_3 }, + { .offset = DP_EDP_DISPLAY_CONTROL_REGISTER, .end = DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB }, + { .offset = DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET, .end = DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET }, +}; + +static int i915_dpcd_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct intel_dp *intel_dp = + enc_to_intel_dp(&intel_attached_encoder(connector)->base); + uint8_t buf[16]; + ssize_t err; + int i; + + if (connector->status != connector_status_connected) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(i915_dpcd_debug); i++) { + const struct dpcd_block *b = &i915_dpcd_debug[i]; + size_t size = b->end ? b->end - b->offset + 1 : (b->size ?: 1); + + if (b->edp && + connector->connector_type != DRM_MODE_CONNECTOR_eDP) + continue; + + /* low tech for now */ + if (WARN_ON(size > sizeof(buf))) + continue; + + err = drm_dp_dpcd_read(&intel_dp->aux, b->offset, buf, size); + if (err <= 0) { + DRM_ERROR("dpcd read (%zu bytes at %u) failed (%zd)\n", + size, b->offset, err); + continue; + } + + seq_printf(m, "%04x: %*ph\n", b->offset, (int) size, buf); + } + + return 0; +} + +static int i915_dpcd_open(struct inode *inode, struct file *file) +{ + return single_open(file, i915_dpcd_show, inode->i_private); +} + +static const struct file_operations i915_dpcd_fops = { + .owner = THIS_MODULE, + .open = i915_dpcd_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * i915_debugfs_connector_add - add i915 specific connector debugfs files + * @connector: pointer to a registered drm_connector + * + * Cleanup will be done by drm_connector_unregister() through a call to + * drm_debugfs_connector_remove(). + * + * Returns 0 on success, negative error codes on error. + */ +int i915_debugfs_connector_add(struct drm_connector *connector) +{ + struct dentry *root = connector->debugfs_entry; + + /* The connector must have been registered beforehands. */ + if (!root) + return -ENODEV; + + if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort || + connector->connector_type == DRM_MODE_CONNECTOR_eDP) + debugfs_create_file("i915_dpcd", S_IRUGO, root, connector, + &i915_dpcd_fops); + + return 0; +} |