aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2014-06-03 23:39:12 -0400
committerDave Airlie <airlied@redhat.com>2014-06-03 23:39:12 -0400
commit1c404d88b26170b82aa274e7a40c91aa33145942 (patch)
tree149e89becf102c010f81bfbe177d9520543ec5f1
parent04381b987292256db6b8530aad193ee0453b72cd (diff)
parent543d3011f1c193ba2257a754757117cafda2252b (diff)
Merge branch 'msm-next' of git://people.freedesktop.org/~robclark/linux into drm-next
Pretty small pull this time around for msm. Adds some useful debugfs I'd been carrying around on a branch for a while, plus few fixes. And Kconfig update for the great ARCH_MSM -> ARCH_QCOM split. * 'msm-next' of git://people.freedesktop.org/~robclark/linux: drm/msm: use correct gfp flag for vram allocation drm/msm/mdp5: fix error return value drm/msm: remove redundant private plane cleanup drm/msm: add perf logging debugfs drm/msm: add rd logging debugfs drm/msm: update for ARCH_MSM -> ARCH_QCOM drm/msm/hdmi: use gpio and HPD polling drm/msm/mdp5: fix crash in error/unload paths
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/msm/Makefile2
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c20
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_connector.c52
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c2
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c2
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c22
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c5
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c47
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h17
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h1
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c1
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c107
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h31
-rw-r--r--drivers/gpu/drm/msm/msm_perf.c275
-rw-r--r--drivers/gpu/drm/msm/msm_rd.c337
16 files changed, 884 insertions, 39 deletions
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index b6984971ce0c..f12388967856 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -3,7 +3,7 @@ config DRM_MSM
3 tristate "MSM DRM" 3 tristate "MSM DRM"
4 depends on DRM 4 depends on DRM
5 depends on MSM_IOMMU 5 depends on MSM_IOMMU
6 depends on ARCH_MSM8960 || (ARM && COMPILE_TEST) 6 depends on ARCH_QCOM || (ARM && COMPILE_TEST)
7 select DRM_KMS_HELPER 7 select DRM_KMS_HELPER
8 select SHMEM 8 select SHMEM
9 select TMPFS 9 select TMPFS
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 5e1e6b0cd8ac..93ca49c8df44 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -34,6 +34,8 @@ msm-y := \
34 msm_gem_submit.o \ 34 msm_gem_submit.o \
35 msm_gpu.o \ 35 msm_gpu.o \
36 msm_iommu.o \ 36 msm_iommu.o \
37 msm_perf.o \
38 msm_rd.o \
37 msm_ringbuffer.o 39 msm_ringbuffer.o
38 40
39msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o 41msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index f20fbde5dc49..942e09d898a8 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -207,11 +207,11 @@ static int a3xx_hw_init(struct msm_gpu *gpu)
207 /* Turn on performance counters: */ 207 /* Turn on performance counters: */
208 gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01); 208 gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01);
209 209
210 /* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS 210 /* Enable the perfcntrs that we use.. */
211 * we will use this to augment our hang detection: 211 for (i = 0; i < gpu->num_perfcntrs; i++) {
212 */ 212 const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
213 gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT, 213 gpu_write(gpu, perfcntr->select_reg, perfcntr->select_val);
214 SP_FS_FULL_ALU_INSTRUCTIONS); 214 }
215 215
216 gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK); 216 gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK);
217 217
@@ -465,6 +465,13 @@ static const struct adreno_gpu_funcs funcs = {
465 }, 465 },
466}; 466};
467 467
468static const struct msm_gpu_perfcntr perfcntrs[] = {
469 { REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO,
470 SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" },
471 { REG_A3XX_SP_PERFCOUNTER7_SELECT, REG_A3XX_RBBM_PERFCTR_SP_7_LO,
472 SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" },
473};
474
468struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) 475struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
469{ 476{
470 struct a3xx_gpu *a3xx_gpu = NULL; 477 struct a3xx_gpu *a3xx_gpu = NULL;
@@ -504,6 +511,9 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
504 DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u", 511 DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
505 gpu->fast_rate, gpu->slow_rate, gpu->bus_freq); 512 gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
506 513
514 gpu->perfcntrs = perfcntrs;
515 gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs);
516
507 ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev); 517 ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev);
508 if (ret) 518 if (ret)
509 goto fail; 519 goto fail;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
index 7dedfdd12075..e56a6196867c 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
@@ -247,36 +247,49 @@ void hdmi_connector_irq(struct drm_connector *connector)
247 } 247 }
248} 248}
249 249
250static enum drm_connector_status detect_reg(struct hdmi *hdmi)
251{
252 uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
253 return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
254 connector_status_connected : connector_status_disconnected;
255}
256
257static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
258{
259 const struct hdmi_platform_config *config = hdmi->config;
260 return gpio_get_value(config->hpd_gpio) ?
261 connector_status_connected :
262 connector_status_disconnected;
263}
264
250static enum drm_connector_status hdmi_connector_detect( 265static enum drm_connector_status hdmi_connector_detect(
251 struct drm_connector *connector, bool force) 266 struct drm_connector *connector, bool force)
252{ 267{
253 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); 268 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
254 struct hdmi *hdmi = hdmi_connector->hdmi; 269 struct hdmi *hdmi = hdmi_connector->hdmi;
255 const struct hdmi_platform_config *config = hdmi->config; 270 enum drm_connector_status stat_gpio, stat_reg;
256 uint32_t hpd_int_status;
257 int retry = 20; 271 int retry = 20;
258 272
259 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); 273 do {
274 stat_gpio = detect_gpio(hdmi);
275 stat_reg = detect_reg(hdmi);
260 276
261 /* sense seems to in some cases be momentarily de-asserted, don't 277 if (stat_gpio == stat_reg)
262 * let that trick us into thinking the monitor is gone:
263 */
264 while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
265 /* hdmi debounce logic seems to get stuck sometimes,
266 * read directly the gpio to get a second opinion:
267 */
268 if (gpio_get_value(config->hpd_gpio)) {
269 DBG("gpio tells us we are connected!");
270 hpd_int_status |= HDMI_HPD_INT_STATUS_CABLE_DETECTED;
271 break; 278 break;
272 } 279
273 mdelay(10); 280 mdelay(10);
274 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); 281 } while (--retry);
275 DBG("status=%08x", hpd_int_status); 282
283 /* the status we get from reading gpio seems to be more reliable,
284 * so trust that one the most if we didn't manage to get hdmi and
285 * gpio status to agree:
286 */
287 if (stat_gpio != stat_reg) {
288 DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
289 DBG("hpd gpio tells us: %d", stat_gpio);
276 } 290 }
277 291
278 return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ? 292 return stat_gpio;
279 connector_status_connected : connector_status_disconnected;
280} 293}
281 294
282static void hdmi_connector_destroy(struct drm_connector *connector) 295static void hdmi_connector_destroy(struct drm_connector *connector)
@@ -389,7 +402,8 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
389 DRM_MODE_CONNECTOR_HDMIA); 402 DRM_MODE_CONNECTOR_HDMIA);
390 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); 403 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
391 404
392 connector->polled = DRM_CONNECTOR_POLL_HPD; 405 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
406 DRM_CONNECTOR_POLL_DISCONNECT;
393 407
394 connector->interlace_allowed = 1; 408 connector->interlace_allowed = 1;
395 connector->doublescan_allowed = 0; 409 connector->doublescan_allowed = 0;
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index ef9957dbac94..74cebb51e8c2 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -217,8 +217,6 @@ static void mdp4_crtc_destroy(struct drm_crtc *crtc)
217{ 217{
218 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); 218 struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
219 219
220 mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane);
221
222 drm_crtc_cleanup(crtc); 220 drm_crtc_cleanup(crtc);
223 drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work); 221 drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);
224 drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work); 222 drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 6ea10bdb6e8f..ebe2e60f3ab1 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -195,8 +195,6 @@ static void mdp5_crtc_destroy(struct drm_crtc *crtc)
195{ 195{
196 struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); 196 struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
197 197
198 mdp5_crtc->plane->funcs->destroy(mdp5_crtc->plane);
199
200 drm_crtc_cleanup(crtc); 198 drm_crtc_cleanup(crtc);
201 drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work); 199 drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work);
202 200
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index ee8446c1b5f6..42caf7fcb0b9 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -280,12 +280,22 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
280 goto fail; 280 goto fail;
281 } 281 }
282 282
283 ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk") || 283 ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk");
284 get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk") || 284 if (ret)
285 get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src") || 285 goto fail;
286 get_clk(pdev, &mdp5_kms->core_clk, "core_clk") || 286 ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk");
287 get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk") || 287 if (ret)
288 get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk"); 288 goto fail;
289 ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src");
290 if (ret)
291 goto fail;
292 ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk");
293 if (ret)
294 goto fail;
295 ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk");
296 if (ret)
297 goto fail;
298 ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
289 if (ret) 299 if (ret)
290 goto fail; 300 goto fail;
291 301
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 47f7bbb9c15a..f3daec4412ad 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -85,8 +85,11 @@ static int mdp5_plane_disable(struct drm_plane *plane)
85static void mdp5_plane_destroy(struct drm_plane *plane) 85static void mdp5_plane_destroy(struct drm_plane *plane)
86{ 86{
87 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); 87 struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
88 struct msm_drm_private *priv = plane->dev->dev_private;
89
90 if (priv->kms)
91 mdp5_plane_disable(plane);
88 92
89 mdp5_plane_disable(plane);
90 drm_plane_cleanup(plane); 93 drm_plane_cleanup(plane);
91 94
92 kfree(mdp5_plane); 95 kfree(mdp5_plane);
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 50ec1bed5820..c071aacf2752 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -220,7 +220,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
220 * is bogus, but non-null if allocation succeeded: 220 * is bogus, but non-null if allocation succeeded:
221 */ 221 */
222 p = dma_alloc_attrs(dev->dev, size, 222 p = dma_alloc_attrs(dev->dev, size,
223 &priv->vram.paddr, 0, &attrs); 223 &priv->vram.paddr, GFP_KERNEL, &attrs);
224 if (!p) { 224 if (!p) {
225 dev_err(dev->dev, "failed to allocate VRAM\n"); 225 dev_err(dev->dev, "failed to allocate VRAM\n");
226 priv->vram.paddr = 0; 226 priv->vram.paddr = 0;
@@ -299,6 +299,10 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
299 priv->fbdev = msm_fbdev_init(dev); 299 priv->fbdev = msm_fbdev_init(dev);
300#endif 300#endif
301 301
302 ret = msm_debugfs_late_init(dev);
303 if (ret)
304 goto fail;
305
302 drm_kms_helper_poll_init(dev); 306 drm_kms_helper_poll_init(dev);
303 307
304 return 0; 308 return 0;
@@ -531,6 +535,41 @@ static struct drm_info_list msm_debugfs_list[] = {
531 { "fb", show_locked, 0, msm_fb_show }, 535 { "fb", show_locked, 0, msm_fb_show },
532}; 536};
533 537
538static int late_init_minor(struct drm_minor *minor)
539{
540 int ret;
541
542 if (!minor)
543 return 0;
544
545 ret = msm_rd_debugfs_init(minor);
546 if (ret) {
547 dev_err(minor->dev->dev, "could not install rd debugfs\n");
548 return ret;
549 }
550
551 ret = msm_perf_debugfs_init(minor);
552 if (ret) {
553 dev_err(minor->dev->dev, "could not install perf debugfs\n");
554 return ret;
555 }
556
557 return 0;
558}
559
560int msm_debugfs_late_init(struct drm_device *dev)
561{
562 int ret;
563 ret = late_init_minor(dev->primary);
564 if (ret)
565 return ret;
566 ret = late_init_minor(dev->render);
567 if (ret)
568 return ret;
569 ret = late_init_minor(dev->control);
570 return ret;
571}
572
534static int msm_debugfs_init(struct drm_minor *minor) 573static int msm_debugfs_init(struct drm_minor *minor)
535{ 574{
536 struct drm_device *dev = minor->dev; 575 struct drm_device *dev = minor->dev;
@@ -545,13 +584,17 @@ static int msm_debugfs_init(struct drm_minor *minor)
545 return ret; 584 return ret;
546 } 585 }
547 586
548 return ret; 587 return 0;
549} 588}
550 589
551static void msm_debugfs_cleanup(struct drm_minor *minor) 590static void msm_debugfs_cleanup(struct drm_minor *minor)
552{ 591{
553 drm_debugfs_remove_files(msm_debugfs_list, 592 drm_debugfs_remove_files(msm_debugfs_list,
554 ARRAY_SIZE(msm_debugfs_list), minor); 593 ARRAY_SIZE(msm_debugfs_list), minor);
594 if (!minor->dev->dev_private)
595 return;
596 msm_rd_debugfs_cleanup(minor);
597 msm_perf_debugfs_cleanup(minor);
555} 598}
556#endif 599#endif
557 600
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 9d10ee0b5aac..8a2c5fd0893e 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -33,7 +33,7 @@
33#include <asm/sizes.h> 33#include <asm/sizes.h>
34 34
35 35
36#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_MSM) 36#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_QCOM)
37/* stubs we need for compile-test: */ 37/* stubs we need for compile-test: */
38static inline struct device *msm_iommu_get_ctx(const char *ctx_name) 38static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
39{ 39{
@@ -55,6 +55,9 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
55struct msm_kms; 55struct msm_kms;
56struct msm_gpu; 56struct msm_gpu;
57struct msm_mmu; 57struct msm_mmu;
58struct msm_rd_state;
59struct msm_perf_state;
60struct msm_gem_submit;
58 61
59#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */ 62#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */
60 63
@@ -82,6 +85,9 @@ struct msm_drm_private {
82 uint32_t next_fence, completed_fence; 85 uint32_t next_fence, completed_fence;
83 wait_queue_head_t fence_event; 86 wait_queue_head_t fence_event;
84 87
88 struct msm_rd_state *rd;
89 struct msm_perf_state *perf;
90
85 /* list of GEM objects: */ 91 /* list of GEM objects: */
86 struct list_head inactive_list; 92 struct list_head inactive_list;
87 93
@@ -204,6 +210,15 @@ void __exit hdmi_unregister(void);
204void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m); 210void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
205void msm_gem_describe_objects(struct list_head *list, struct seq_file *m); 211void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
206void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); 212void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
213int msm_debugfs_late_init(struct drm_device *dev);
214int msm_rd_debugfs_init(struct drm_minor *minor);
215void msm_rd_debugfs_cleanup(struct drm_minor *minor);
216void msm_rd_dump_submit(struct msm_gem_submit *submit);
217int msm_perf_debugfs_init(struct drm_minor *minor);
218void msm_perf_debugfs_cleanup(struct drm_minor *minor);
219#else
220static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; }
221static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {}
207#endif 222#endif
208 223
209void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, 224void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 3246bb46c4f2..bfb052688f8e 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -90,6 +90,7 @@ struct msm_gem_submit {
90 uint32_t type; 90 uint32_t type;
91 uint32_t size; /* in dwords */ 91 uint32_t size; /* in dwords */
92 uint32_t iova; 92 uint32_t iova;
93 uint32_t idx; /* cmdstream buffer idx in bos[] */
93 } cmd[MAX_CMDS]; 94 } cmd[MAX_CMDS];
94 struct { 95 struct {
95 uint32_t flags; 96 uint32_t flags;
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 1f1f4cffdaed..cd0554f68316 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -402,6 +402,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
402 submit->cmd[i].type = submit_cmd.type; 402 submit->cmd[i].type = submit_cmd.type;
403 submit->cmd[i].size = submit_cmd.size / 4; 403 submit->cmd[i].size = submit_cmd.size / 4;
404 submit->cmd[i].iova = iova + submit_cmd.submit_offset; 404 submit->cmd[i].iova = iova + submit_cmd.submit_offset;
405 submit->cmd[i].idx = submit_cmd.submit_idx;
405 406
406 if (submit->valid) 407 if (submit->valid)
407 continue; 408 continue;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 3e667ca1f2b9..c6322197db8c 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -320,6 +320,101 @@ static void hangcheck_handler(unsigned long data)
320} 320}
321 321
322/* 322/*
323 * Performance Counters:
324 */
325
326/* called under perf_lock */
327static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs)
328{
329 uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)];
330 int i, n = min(ncntrs, gpu->num_perfcntrs);
331
332 /* read current values: */
333 for (i = 0; i < gpu->num_perfcntrs; i++)
334 current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg);
335
336 /* update cntrs: */
337 for (i = 0; i < n; i++)
338 cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i];
339
340 /* save current values: */
341 for (i = 0; i < gpu->num_perfcntrs; i++)
342 gpu->last_cntrs[i] = current_cntrs[i];
343
344 return n;
345}
346
347static void update_sw_cntrs(struct msm_gpu *gpu)
348{
349 ktime_t time;
350 uint32_t elapsed;
351 unsigned long flags;
352
353 spin_lock_irqsave(&gpu->perf_lock, flags);
354 if (!gpu->perfcntr_active)
355 goto out;
356
357 time = ktime_get();
358 elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time));
359
360 gpu->totaltime += elapsed;
361 if (gpu->last_sample.active)
362 gpu->activetime += elapsed;
363
364 gpu->last_sample.active = msm_gpu_active(gpu);
365 gpu->last_sample.time = time;
366
367out:
368 spin_unlock_irqrestore(&gpu->perf_lock, flags);
369}
370
371void msm_gpu_perfcntr_start(struct msm_gpu *gpu)
372{
373 unsigned long flags;
374
375 spin_lock_irqsave(&gpu->perf_lock, flags);
376 /* we could dynamically enable/disable perfcntr registers too.. */
377 gpu->last_sample.active = msm_gpu_active(gpu);
378 gpu->last_sample.time = ktime_get();
379 gpu->activetime = gpu->totaltime = 0;
380 gpu->perfcntr_active = true;
381 update_hw_cntrs(gpu, 0, NULL);
382 spin_unlock_irqrestore(&gpu->perf_lock, flags);
383}
384
385void msm_gpu_perfcntr_stop(struct msm_gpu *gpu)
386{
387 gpu->perfcntr_active = false;
388}
389
390/* returns -errno or # of cntrs sampled */
391int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
392 uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs)
393{
394 unsigned long flags;
395 int ret;
396
397 spin_lock_irqsave(&gpu->perf_lock, flags);
398
399 if (!gpu->perfcntr_active) {
400 ret = -EINVAL;
401 goto out;
402 }
403
404 *activetime = gpu->activetime;
405 *totaltime = gpu->totaltime;
406
407 gpu->activetime = gpu->totaltime = 0;
408
409 ret = update_hw_cntrs(gpu, ncntrs, cntrs);
410
411out:
412 spin_unlock_irqrestore(&gpu->perf_lock, flags);
413
414 return ret;
415}
416
417/*
323 * Cmdstream submission/retirement: 418 * Cmdstream submission/retirement:
324 */ 419 */
325 420
@@ -361,6 +456,7 @@ void msm_gpu_retire(struct msm_gpu *gpu)
361{ 456{
362 struct msm_drm_private *priv = gpu->dev->dev_private; 457 struct msm_drm_private *priv = gpu->dev->dev_private;
363 queue_work(priv->wq, &gpu->retire_work); 458 queue_work(priv->wq, &gpu->retire_work);
459 update_sw_cntrs(gpu);
364} 460}
365 461
366/* add bo's to gpu's ring, and kick gpu: */ 462/* add bo's to gpu's ring, and kick gpu: */
@@ -377,6 +473,12 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
377 473
378 inactive_cancel(gpu); 474 inactive_cancel(gpu);
379 475
476 msm_rd_dump_submit(submit);
477
478 gpu->submitted_fence = submit->fence;
479
480 update_sw_cntrs(gpu);
481
380 ret = gpu->funcs->submit(gpu, submit, ctx); 482 ret = gpu->funcs->submit(gpu, submit, ctx);
381 priv->lastctx = ctx; 483 priv->lastctx = ctx;
382 484
@@ -429,6 +531,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
429 struct iommu_domain *iommu; 531 struct iommu_domain *iommu;
430 int i, ret; 532 int i, ret;
431 533
534 if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
535 gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
536
432 gpu->dev = drm; 537 gpu->dev = drm;
433 gpu->funcs = funcs; 538 gpu->funcs = funcs;
434 gpu->name = name; 539 gpu->name = name;
@@ -444,6 +549,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
444 setup_timer(&gpu->hangcheck_timer, hangcheck_handler, 549 setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
445 (unsigned long)gpu); 550 (unsigned long)gpu);
446 551
552 spin_lock_init(&gpu->perf_lock);
553
447 BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks)); 554 BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));
448 555
449 /* Map registers: */ 556 /* Map registers: */
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index fad27008922f..9b579b792840 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -25,6 +25,7 @@
25#include "msm_ringbuffer.h" 25#include "msm_ringbuffer.h"
26 26
27struct msm_gem_submit; 27struct msm_gem_submit;
28struct msm_gpu_perfcntr;
28 29
29/* So far, with hardware that I've seen to date, we can have: 30/* So far, with hardware that I've seen to date, we can have:
30 * + zero, one, or two z180 2d cores 31 * + zero, one, or two z180 2d cores
@@ -64,6 +65,18 @@ struct msm_gpu {
64 struct drm_device *dev; 65 struct drm_device *dev;
65 const struct msm_gpu_funcs *funcs; 66 const struct msm_gpu_funcs *funcs;
66 67
68 /* performance counters (hw & sw): */
69 spinlock_t perf_lock;
70 bool perfcntr_active;
71 struct {
72 bool active;
73 ktime_t time;
74 } last_sample;
75 uint32_t totaltime, activetime; /* sw counters */
76 uint32_t last_cntrs[5]; /* hw counters */
77 const struct msm_gpu_perfcntr *perfcntrs;
78 uint32_t num_perfcntrs;
79
67 struct msm_ringbuffer *rb; 80 struct msm_ringbuffer *rb;
68 uint32_t rb_iova; 81 uint32_t rb_iova;
69 82
@@ -113,6 +126,19 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu)
113 return gpu->submitted_fence > gpu->funcs->last_fence(gpu); 126 return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
114} 127}
115 128
129/* Perf-Counters:
130 * The select_reg and select_val are just there for the benefit of the child
131 * class that actually enables the perf counter.. but msm_gpu base class
132 * will handle sampling/displaying the counters.
133 */
134
135struct msm_gpu_perfcntr {
136 uint32_t select_reg;
137 uint32_t sample_reg;
138 uint32_t select_val;
139 const char *name;
140};
141
116static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) 142static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
117{ 143{
118 msm_writel(data, gpu->mmio + (reg << 2)); 144 msm_writel(data, gpu->mmio + (reg << 2));
@@ -126,6 +152,11 @@ static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)
126int msm_gpu_pm_suspend(struct msm_gpu *gpu); 152int msm_gpu_pm_suspend(struct msm_gpu *gpu);
127int msm_gpu_pm_resume(struct msm_gpu *gpu); 153int msm_gpu_pm_resume(struct msm_gpu *gpu);
128 154
155void msm_gpu_perfcntr_start(struct msm_gpu *gpu);
156void msm_gpu_perfcntr_stop(struct msm_gpu *gpu);
157int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
158 uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs);
159
129void msm_gpu_retire(struct msm_gpu *gpu); 160void msm_gpu_retire(struct msm_gpu *gpu);
130int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, 161int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
131 struct msm_file_private *ctx); 162 struct msm_file_private *ctx);
diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c
new file mode 100644
index 000000000000..830857c47c86
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_perf.c
@@ -0,0 +1,275 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* For profiling, userspace can:
19 *
20 * tail -f /sys/kernel/debug/dri/<minor>/gpu
21 *
22 * This will enable performance counters/profiling to track the busy time
23 * and any gpu specific performance counters that are supported.
24 */
25
26#ifdef CONFIG_DEBUG_FS
27
28#include <linux/debugfs.h>
29
30#include "msm_drv.h"
31#include "msm_gpu.h"
32
33struct msm_perf_state {
34 struct drm_device *dev;
35
36 bool open;
37 int cnt;
38 struct mutex read_lock;
39
40 char buf[256];
41 int buftot, bufpos;
42
43 unsigned long next_jiffies;
44
45 struct dentry *ent;
46 struct drm_info_node *node;
47};
48
49#define SAMPLE_TIME (HZ/4)
50
51/* wait for next sample time: */
52static int wait_sample(struct msm_perf_state *perf)
53{
54 unsigned long start_jiffies = jiffies;
55
56 if (time_after(perf->next_jiffies, start_jiffies)) {
57 unsigned long remaining_jiffies =
58 perf->next_jiffies - start_jiffies;
59 int ret = schedule_timeout_interruptible(remaining_jiffies);
60 if (ret > 0) {
61 /* interrupted */
62 return -ERESTARTSYS;
63 }
64 }
65 perf->next_jiffies += SAMPLE_TIME;
66 return 0;
67}
68
69static int refill_buf(struct msm_perf_state *perf)
70{
71 struct msm_drm_private *priv = perf->dev->dev_private;
72 struct msm_gpu *gpu = priv->gpu;
73 char *ptr = perf->buf;
74 int rem = sizeof(perf->buf);
75 int i, n;
76
77 if ((perf->cnt++ % 32) == 0) {
78 /* Header line: */
79 n = snprintf(ptr, rem, "%%BUSY");
80 ptr += n;
81 rem -= n;
82
83 for (i = 0; i < gpu->num_perfcntrs; i++) {
84 const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
85 n = snprintf(ptr, rem, "\t%s", perfcntr->name);
86 ptr += n;
87 rem -= n;
88 }
89 } else {
90 /* Sample line: */
91 uint32_t activetime = 0, totaltime = 0;
92 uint32_t cntrs[5];
93 uint32_t val;
94 int ret;
95
96 /* sleep until next sample time: */
97 ret = wait_sample(perf);
98 if (ret)
99 return ret;
100
101 ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime,
102 ARRAY_SIZE(cntrs), cntrs);
103 if (ret < 0)
104 return ret;
105
106 val = totaltime ? 1000 * activetime / totaltime : 0;
107 n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10);
108 ptr += n;
109 rem -= n;
110
111 for (i = 0; i < ret; i++) {
112 /* cycle counters (I think).. convert to MHz.. */
113 val = cntrs[i] / 10000;
114 n = snprintf(ptr, rem, "\t%5d.%02d",
115 val / 100, val % 100);
116 ptr += n;
117 rem -= n;
118 }
119 }
120
121 n = snprintf(ptr, rem, "\n");
122 ptr += n;
123 rem -= n;
124
125 perf->bufpos = 0;
126 perf->buftot = ptr - perf->buf;
127
128 return 0;
129}
130
131static ssize_t perf_read(struct file *file, char __user *buf,
132 size_t sz, loff_t *ppos)
133{
134 struct msm_perf_state *perf = file->private_data;
135 int n = 0, ret;
136
137 mutex_lock(&perf->read_lock);
138
139 if (perf->bufpos >= perf->buftot) {
140 ret = refill_buf(perf);
141 if (ret)
142 goto out;
143 }
144
145 n = min((int)sz, perf->buftot - perf->bufpos);
146 ret = copy_to_user(buf, &perf->buf[perf->bufpos], n);
147 if (ret)
148 goto out;
149
150 perf->bufpos += n;
151 *ppos += n;
152
153out:
154 mutex_unlock(&perf->read_lock);
155 if (ret)
156 return ret;
157 return n;
158}
159
160static int perf_open(struct inode *inode, struct file *file)
161{
162 struct msm_perf_state *perf = inode->i_private;
163 struct drm_device *dev = perf->dev;
164 struct msm_drm_private *priv = dev->dev_private;
165 struct msm_gpu *gpu = priv->gpu;
166 int ret = 0;
167
168 mutex_lock(&dev->struct_mutex);
169
170 if (perf->open || !gpu) {
171 ret = -EBUSY;
172 goto out;
173 }
174
175 file->private_data = perf;
176 perf->open = true;
177 perf->cnt = 0;
178 perf->buftot = 0;
179 perf->bufpos = 0;
180 msm_gpu_perfcntr_start(gpu);
181 perf->next_jiffies = jiffies + SAMPLE_TIME;
182
183out:
184 mutex_unlock(&dev->struct_mutex);
185 return ret;
186}
187
188static int perf_release(struct inode *inode, struct file *file)
189{
190 struct msm_perf_state *perf = inode->i_private;
191 struct msm_drm_private *priv = perf->dev->dev_private;
192 msm_gpu_perfcntr_stop(priv->gpu);
193 perf->open = false;
194 return 0;
195}
196
197
198static const struct file_operations perf_debugfs_fops = {
199 .owner = THIS_MODULE,
200 .open = perf_open,
201 .read = perf_read,
202 .llseek = no_llseek,
203 .release = perf_release,
204};
205
206int msm_perf_debugfs_init(struct drm_minor *minor)
207{
208 struct msm_drm_private *priv = minor->dev->dev_private;
209 struct msm_perf_state *perf;
210
211 /* only create on first minor: */
212 if (priv->perf)
213 return 0;
214
215 perf = kzalloc(sizeof(*perf), GFP_KERNEL);
216 if (!perf)
217 return -ENOMEM;
218
219 perf->dev = minor->dev;
220
221 mutex_init(&perf->read_lock);
222 priv->perf = perf;
223
224 perf->node = kzalloc(sizeof(*perf->node), GFP_KERNEL);
225 if (!perf->node)
226 goto fail;
227
228 perf->ent = debugfs_create_file("perf", S_IFREG | S_IRUGO,
229 minor->debugfs_root, perf, &perf_debugfs_fops);
230 if (!perf->ent) {
231 DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/perf\n",
232 minor->debugfs_root->d_name.name);
233 goto fail;
234 }
235
236 perf->node->minor = minor;
237 perf->node->dent = perf->ent;
238 perf->node->info_ent = NULL;
239
240 mutex_lock(&minor->debugfs_lock);
241 list_add(&perf->node->list, &minor->debugfs_list);
242 mutex_unlock(&minor->debugfs_lock);
243
244 return 0;
245
246fail:
247 msm_perf_debugfs_cleanup(minor);
248 return -1;
249}
250
251void msm_perf_debugfs_cleanup(struct drm_minor *minor)
252{
253 struct msm_drm_private *priv = minor->dev->dev_private;
254 struct msm_perf_state *perf = priv->perf;
255
256 if (!perf)
257 return;
258
259 priv->perf = NULL;
260
261 debugfs_remove(perf->ent);
262
263 if (perf->node) {
264 mutex_lock(&minor->debugfs_lock);
265 list_del(&perf->node->list);
266 mutex_unlock(&minor->debugfs_lock);
267 kfree(perf->node);
268 }
269
270 mutex_destroy(&perf->read_lock);
271
272 kfree(perf);
273}
274
275#endif
diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c
new file mode 100644
index 000000000000..9a78c48817c6
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_rd.c
@@ -0,0 +1,337 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* For debugging crashes, userspace can:
19 *
20 * tail -f /sys/kernel/debug/dri/<minor>/rd > logfile.rd
21 *
22 * To log the cmdstream in a format that is understood by freedreno/cffdump
23 * utility. By comparing the last successfully completed fence #, to the
24 * cmdstream for the next fence, you can narrow down which process and submit
25 * caused the gpu crash/lockup.
26 *
27 * This bypasses drm_debugfs_create_files() mainly because we need to use
28 * our own fops for a bit more control. In particular, we don't want to
29 * do anything if userspace doesn't have the debugfs file open.
30 */
31
32#ifdef CONFIG_DEBUG_FS
33
34#include <linux/kfifo.h>
35#include <linux/debugfs.h>
36#include <linux/circ_buf.h>
37#include <linux/wait.h>
38
39#include "msm_drv.h"
40#include "msm_gpu.h"
41#include "msm_gem.h"
42
43enum rd_sect_type {
44 RD_NONE,
45 RD_TEST, /* ascii text */
46 RD_CMD, /* ascii text */
47 RD_GPUADDR, /* u32 gpuaddr, u32 size */
48 RD_CONTEXT, /* raw dump */
49 RD_CMDSTREAM, /* raw dump */
50 RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
51 RD_PARAM, /* u32 param_type, u32 param_val, u32 bitlen */
52 RD_FLUSH, /* empty, clear previous params */
53 RD_PROGRAM, /* shader program, raw dump */
54 RD_VERT_SHADER,
55 RD_FRAG_SHADER,
56 RD_BUFFER_CONTENTS,
57 RD_GPU_ID,
58};
59
60#define BUF_SZ 512 /* should be power of 2 */
61
62/* space used: */
63#define circ_count(circ) \
64 (CIRC_CNT((circ)->head, (circ)->tail, BUF_SZ))
65#define circ_count_to_end(circ) \
66 (CIRC_CNT_TO_END((circ)->head, (circ)->tail, BUF_SZ))
67/* space available: */
68#define circ_space(circ) \
69 (CIRC_SPACE((circ)->head, (circ)->tail, BUF_SZ))
70#define circ_space_to_end(circ) \
71 (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, BUF_SZ))
72
73struct msm_rd_state {
74 struct drm_device *dev;
75
76 bool open;
77
78 struct dentry *ent;
79 struct drm_info_node *node;
80
81 /* current submit to read out: */
82 struct msm_gem_submit *submit;
83
84 /* fifo access is synchronized on the producer side by
85 * struct_mutex held by submit code (otherwise we could
86 * end up w/ cmds logged in different order than they
87 * were executed). And read_lock synchronizes the reads
88 */
89 struct mutex read_lock;
90
91 wait_queue_head_t fifo_event;
92 struct circ_buf fifo;
93
94 char buf[BUF_SZ];
95};
96
97static void rd_write(struct msm_rd_state *rd, const void *buf, int sz)
98{
99 struct circ_buf *fifo = &rd->fifo;
100 const char *ptr = buf;
101
102 while (sz > 0) {
103 char *fptr = &fifo->buf[fifo->head];
104 int n;
105
106 wait_event(rd->fifo_event, circ_space(&rd->fifo) > 0);
107
108 n = min(sz, circ_space_to_end(&rd->fifo));
109 memcpy(fptr, ptr, n);
110
111 fifo->head = (fifo->head + n) & (BUF_SZ - 1);
112 sz -= n;
113 ptr += n;
114
115 wake_up_all(&rd->fifo_event);
116 }
117}
118
119static void rd_write_section(struct msm_rd_state *rd,
120 enum rd_sect_type type, const void *buf, int sz)
121{
122 rd_write(rd, &type, 4);
123 rd_write(rd, &sz, 4);
124 rd_write(rd, buf, sz);
125}
126
127static ssize_t rd_read(struct file *file, char __user *buf,
128 size_t sz, loff_t *ppos)
129{
130 struct msm_rd_state *rd = file->private_data;
131 struct circ_buf *fifo = &rd->fifo;
132 const char *fptr = &fifo->buf[fifo->tail];
133 int n = 0, ret = 0;
134
135 mutex_lock(&rd->read_lock);
136
137 ret = wait_event_interruptible(rd->fifo_event,
138 circ_count(&rd->fifo) > 0);
139 if (ret)
140 goto out;
141
142 n = min_t(int, sz, circ_count_to_end(&rd->fifo));
143 ret = copy_to_user(buf, fptr, n);
144 if (ret)
145 goto out;
146
147 fifo->tail = (fifo->tail + n) & (BUF_SZ - 1);
148 *ppos += n;
149
150 wake_up_all(&rd->fifo_event);
151
152out:
153 mutex_unlock(&rd->read_lock);
154 if (ret)
155 return ret;
156 return n;
157}
158
159static int rd_open(struct inode *inode, struct file *file)
160{
161 struct msm_rd_state *rd = inode->i_private;
162 struct drm_device *dev = rd->dev;
163 struct msm_drm_private *priv = dev->dev_private;
164 struct msm_gpu *gpu = priv->gpu;
165 uint64_t val;
166 uint32_t gpu_id;
167 int ret = 0;
168
169 mutex_lock(&dev->struct_mutex);
170
171 if (rd->open || !gpu) {
172 ret = -EBUSY;
173 goto out;
174 }
175
176 file->private_data = rd;
177 rd->open = true;
178
179 /* the parsing tools need to know gpu-id to know which
180 * register database to load.
181 */
182 gpu->funcs->get_param(gpu, MSM_PARAM_GPU_ID, &val);
183 gpu_id = val;
184
185 rd_write_section(rd, RD_GPU_ID, &gpu_id, sizeof(gpu_id));
186
187out:
188 mutex_unlock(&dev->struct_mutex);
189 return ret;
190}
191
192static int rd_release(struct inode *inode, struct file *file)
193{
194 struct msm_rd_state *rd = inode->i_private;
195 rd->open = false;
196 return 0;
197}
198
199
200static const struct file_operations rd_debugfs_fops = {
201 .owner = THIS_MODULE,
202 .open = rd_open,
203 .read = rd_read,
204 .llseek = no_llseek,
205 .release = rd_release,
206};
207
208int msm_rd_debugfs_init(struct drm_minor *minor)
209{
210 struct msm_drm_private *priv = minor->dev->dev_private;
211 struct msm_rd_state *rd;
212
213 /* only create on first minor: */
214 if (priv->rd)
215 return 0;
216
217 rd = kzalloc(sizeof(*rd), GFP_KERNEL);
218 if (!rd)
219 return -ENOMEM;
220
221 rd->dev = minor->dev;
222 rd->fifo.buf = rd->buf;
223
224 mutex_init(&rd->read_lock);
225 priv->rd = rd;
226
227 init_waitqueue_head(&rd->fifo_event);
228
229 rd->node = kzalloc(sizeof(*rd->node), GFP_KERNEL);
230 if (!rd->node)
231 goto fail;
232
233 rd->ent = debugfs_create_file("rd", S_IFREG | S_IRUGO,
234 minor->debugfs_root, rd, &rd_debugfs_fops);
235 if (!rd->ent) {
236 DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/rd\n",
237 minor->debugfs_root->d_name.name);
238 goto fail;
239 }
240
241 rd->node->minor = minor;
242 rd->node->dent = rd->ent;
243 rd->node->info_ent = NULL;
244
245 mutex_lock(&minor->debugfs_lock);
246 list_add(&rd->node->list, &minor->debugfs_list);
247 mutex_unlock(&minor->debugfs_lock);
248
249 return 0;
250
251fail:
252 msm_rd_debugfs_cleanup(minor);
253 return -1;
254}
255
256void msm_rd_debugfs_cleanup(struct drm_minor *minor)
257{
258 struct msm_drm_private *priv = minor->dev->dev_private;
259 struct msm_rd_state *rd = priv->rd;
260
261 if (!rd)
262 return;
263
264 priv->rd = NULL;
265
266 debugfs_remove(rd->ent);
267
268 if (rd->node) {
269 mutex_lock(&minor->debugfs_lock);
270 list_del(&rd->node->list);
271 mutex_unlock(&minor->debugfs_lock);
272 kfree(rd->node);
273 }
274
275 mutex_destroy(&rd->read_lock);
276
277 kfree(rd);
278}
279
280/* called under struct_mutex */
281void msm_rd_dump_submit(struct msm_gem_submit *submit)
282{
283 struct drm_device *dev = submit->dev;
284 struct msm_drm_private *priv = dev->dev_private;
285 struct msm_rd_state *rd = priv->rd;
286 char msg[128];
287 int i, n;
288
289 if (!rd->open)
290 return;
291
292 /* writing into fifo is serialized by caller, and
293 * rd->read_lock is used to serialize the reads
294 */
295 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
296
297 n = snprintf(msg, sizeof(msg), "%.*s/%d: fence=%u",
298 TASK_COMM_LEN, current->comm, task_pid_nr(current),
299 submit->fence);
300
301 rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
302
303 /* could be nice to have an option (module-param?) to snapshot
304 * all the bo's associated with the submit. Handy to see vtx
305 * buffers, etc. For now just the cmdstream bo's is enough.
306 */
307
308 for (i = 0; i < submit->nr_cmds; i++) {
309 uint32_t idx = submit->cmd[i].idx;
310 uint32_t iova = submit->cmd[i].iova;
311 uint32_t szd = submit->cmd[i].size; /* in dwords */
312 struct msm_gem_object *obj = submit->bos[idx].obj;
313 const char *buf = msm_gem_vaddr_locked(&obj->base);
314
315 buf += iova - submit->bos[idx].iova;
316
317 rd_write_section(rd, RD_GPUADDR,
318 (uint32_t[2]){ iova, szd * 4 }, 8);
319 rd_write_section(rd, RD_BUFFER_CONTENTS,
320 buf, szd * 4);
321
322 switch (submit->cmd[i].type) {
323 case MSM_SUBMIT_CMD_IB_TARGET_BUF:
324 /* ignore IB-targets, we've logged the buffer, the
325 * parser tool will follow the IB based on the logged
326 * buffer/gpuaddr, so nothing more to do.
327 */
328 break;
329 case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
330 case MSM_SUBMIT_CMD_BUF:
331 rd_write_section(rd, RD_CMDSTREAM_ADDR,
332 (uint32_t[2]){ iova, szd }, 8);
333 break;
334 }
335 }
336}
337#endif