mirror of
https://github.com/torvalds/linux.git
synced 2024-12-13 22:53:20 +00:00
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
This commit is contained in:
commit
1c404d88b2
@ -3,7 +3,7 @@ config DRM_MSM
|
||||
tristate "MSM DRM"
|
||||
depends on DRM
|
||||
depends on MSM_IOMMU
|
||||
depends on ARCH_MSM8960 || (ARM && COMPILE_TEST)
|
||||
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
|
||||
select DRM_KMS_HELPER
|
||||
select SHMEM
|
||||
select TMPFS
|
||||
|
@ -34,6 +34,8 @@ msm-y := \
|
||||
msm_gem_submit.o \
|
||||
msm_gpu.o \
|
||||
msm_iommu.o \
|
||||
msm_perf.o \
|
||||
msm_rd.o \
|
||||
msm_ringbuffer.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
|
||||
|
@ -207,11 +207,11 @@ static int a3xx_hw_init(struct msm_gpu *gpu)
|
||||
/* Turn on performance counters: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01);
|
||||
|
||||
/* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
|
||||
* we will use this to augment our hang detection:
|
||||
*/
|
||||
gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT,
|
||||
SP_FS_FULL_ALU_INSTRUCTIONS);
|
||||
/* Enable the perfcntrs that we use.. */
|
||||
for (i = 0; i < gpu->num_perfcntrs; i++) {
|
||||
const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
|
||||
gpu_write(gpu, perfcntr->select_reg, perfcntr->select_val);
|
||||
}
|
||||
|
||||
gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK);
|
||||
|
||||
@ -465,6 +465,13 @@ static const struct adreno_gpu_funcs funcs = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct msm_gpu_perfcntr perfcntrs[] = {
|
||||
{ REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO,
|
||||
SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" },
|
||||
{ REG_A3XX_SP_PERFCOUNTER7_SELECT, REG_A3XX_RBBM_PERFCTR_SP_7_LO,
|
||||
SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" },
|
||||
};
|
||||
|
||||
struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
|
||||
{
|
||||
struct a3xx_gpu *a3xx_gpu = NULL;
|
||||
@ -504,6 +511,9 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
|
||||
DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
|
||||
gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
|
||||
|
||||
gpu->perfcntrs = perfcntrs;
|
||||
gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs);
|
||||
|
||||
ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
@ -247,36 +247,49 @@ void hdmi_connector_irq(struct drm_connector *connector)
|
||||
}
|
||||
}
|
||||
|
||||
static enum drm_connector_status detect_reg(struct hdmi *hdmi)
|
||||
{
|
||||
uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
|
||||
return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
}
|
||||
|
||||
static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
|
||||
{
|
||||
const struct hdmi_platform_config *config = hdmi->config;
|
||||
return gpio_get_value(config->hpd_gpio) ?
|
||||
connector_status_connected :
|
||||
connector_status_disconnected;
|
||||
}
|
||||
|
||||
static enum drm_connector_status hdmi_connector_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
|
||||
struct hdmi *hdmi = hdmi_connector->hdmi;
|
||||
const struct hdmi_platform_config *config = hdmi->config;
|
||||
uint32_t hpd_int_status;
|
||||
enum drm_connector_status stat_gpio, stat_reg;
|
||||
int retry = 20;
|
||||
|
||||
hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
|
||||
do {
|
||||
stat_gpio = detect_gpio(hdmi);
|
||||
stat_reg = detect_reg(hdmi);
|
||||
|
||||
/* sense seems to in some cases be momentarily de-asserted, don't
|
||||
* let that trick us into thinking the monitor is gone:
|
||||
*/
|
||||
while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
|
||||
/* hdmi debounce logic seems to get stuck sometimes,
|
||||
* read directly the gpio to get a second opinion:
|
||||
*/
|
||||
if (gpio_get_value(config->hpd_gpio)) {
|
||||
DBG("gpio tells us we are connected!");
|
||||
hpd_int_status |= HDMI_HPD_INT_STATUS_CABLE_DETECTED;
|
||||
if (stat_gpio == stat_reg)
|
||||
break;
|
||||
}
|
||||
|
||||
mdelay(10);
|
||||
hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
|
||||
DBG("status=%08x", hpd_int_status);
|
||||
} while (--retry);
|
||||
|
||||
/* the status we get from reading gpio seems to be more reliable,
|
||||
* so trust that one the most if we didn't manage to get hdmi and
|
||||
* gpio status to agree:
|
||||
*/
|
||||
if (stat_gpio != stat_reg) {
|
||||
DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
|
||||
DBG("hpd gpio tells us: %d", stat_gpio);
|
||||
}
|
||||
|
||||
return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
return stat_gpio;
|
||||
}
|
||||
|
||||
static void hdmi_connector_destroy(struct drm_connector *connector)
|
||||
@ -389,7 +402,8 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
|
||||
DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
connector->interlace_allowed = 1;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
@ -217,8 +217,6 @@ static void mdp4_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
|
||||
mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane);
|
||||
|
||||
drm_crtc_cleanup(crtc);
|
||||
drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);
|
||||
drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work);
|
||||
|
@ -195,8 +195,6 @@ static void mdp5_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
||||
|
||||
mdp5_crtc->plane->funcs->destroy(mdp5_crtc->plane);
|
||||
|
||||
drm_crtc_cleanup(crtc);
|
||||
drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work);
|
||||
|
||||
|
@ -280,12 +280,22 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk") ||
|
||||
get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk") ||
|
||||
get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src") ||
|
||||
get_clk(pdev, &mdp5_kms->core_clk, "core_clk") ||
|
||||
get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk") ||
|
||||
get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
|
||||
ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk");
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk");
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src");
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk");
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk");
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
|
@ -85,8 +85,11 @@ static int mdp5_plane_disable(struct drm_plane *plane)
|
||||
static void mdp5_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
|
||||
struct msm_drm_private *priv = plane->dev->dev_private;
|
||||
|
||||
if (priv->kms)
|
||||
mdp5_plane_disable(plane);
|
||||
|
||||
mdp5_plane_disable(plane);
|
||||
drm_plane_cleanup(plane);
|
||||
|
||||
kfree(mdp5_plane);
|
||||
|
@ -220,7 +220,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
|
||||
* is bogus, but non-null if allocation succeeded:
|
||||
*/
|
||||
p = dma_alloc_attrs(dev->dev, size,
|
||||
&priv->vram.paddr, 0, &attrs);
|
||||
&priv->vram.paddr, GFP_KERNEL, &attrs);
|
||||
if (!p) {
|
||||
dev_err(dev->dev, "failed to allocate VRAM\n");
|
||||
priv->vram.paddr = 0;
|
||||
@ -299,6 +299,10 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
|
||||
priv->fbdev = msm_fbdev_init(dev);
|
||||
#endif
|
||||
|
||||
ret = msm_debugfs_late_init(dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
return 0;
|
||||
@ -531,6 +535,41 @@ static struct drm_info_list msm_debugfs_list[] = {
|
||||
{ "fb", show_locked, 0, msm_fb_show },
|
||||
};
|
||||
|
||||
static int late_init_minor(struct drm_minor *minor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!minor)
|
||||
return 0;
|
||||
|
||||
ret = msm_rd_debugfs_init(minor);
|
||||
if (ret) {
|
||||
dev_err(minor->dev->dev, "could not install rd debugfs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = msm_perf_debugfs_init(minor);
|
||||
if (ret) {
|
||||
dev_err(minor->dev->dev, "could not install perf debugfs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_debugfs_late_init(struct drm_device *dev)
|
||||
{
|
||||
int ret;
|
||||
ret = late_init_minor(dev->primary);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = late_init_minor(dev->render);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = late_init_minor(dev->control);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
struct drm_device *dev = minor->dev;
|
||||
@ -545,13 +584,17 @@ static int msm_debugfs_init(struct drm_minor *minor)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_debugfs_cleanup(struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(msm_debugfs_list,
|
||||
ARRAY_SIZE(msm_debugfs_list), minor);
|
||||
if (!minor->dev->dev_private)
|
||||
return;
|
||||
msm_rd_debugfs_cleanup(minor);
|
||||
msm_perf_debugfs_cleanup(minor);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include <asm/sizes.h>
|
||||
|
||||
|
||||
#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_MSM)
|
||||
#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_QCOM)
|
||||
/* stubs we need for compile-test: */
|
||||
static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
|
||||
{
|
||||
@ -55,6 +55,9 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
|
||||
struct msm_kms;
|
||||
struct msm_gpu;
|
||||
struct msm_mmu;
|
||||
struct msm_rd_state;
|
||||
struct msm_perf_state;
|
||||
struct msm_gem_submit;
|
||||
|
||||
#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */
|
||||
|
||||
@ -82,6 +85,9 @@ struct msm_drm_private {
|
||||
uint32_t next_fence, completed_fence;
|
||||
wait_queue_head_t fence_event;
|
||||
|
||||
struct msm_rd_state *rd;
|
||||
struct msm_perf_state *perf;
|
||||
|
||||
/* list of GEM objects: */
|
||||
struct list_head inactive_list;
|
||||
|
||||
@ -204,6 +210,15 @@ void __exit hdmi_unregister(void);
|
||||
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
|
||||
void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
|
||||
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
|
||||
int msm_debugfs_late_init(struct drm_device *dev);
|
||||
int msm_rd_debugfs_init(struct drm_minor *minor);
|
||||
void msm_rd_debugfs_cleanup(struct drm_minor *minor);
|
||||
void msm_rd_dump_submit(struct msm_gem_submit *submit);
|
||||
int msm_perf_debugfs_init(struct drm_minor *minor);
|
||||
void msm_perf_debugfs_cleanup(struct drm_minor *minor);
|
||||
#else
|
||||
static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; }
|
||||
static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {}
|
||||
#endif
|
||||
|
||||
void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
|
||||
|
@ -90,6 +90,7 @@ struct msm_gem_submit {
|
||||
uint32_t type;
|
||||
uint32_t size; /* in dwords */
|
||||
uint32_t iova;
|
||||
uint32_t idx; /* cmdstream buffer idx in bos[] */
|
||||
} cmd[MAX_CMDS];
|
||||
struct {
|
||||
uint32_t flags;
|
||||
|
@ -402,6 +402,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
submit->cmd[i].type = submit_cmd.type;
|
||||
submit->cmd[i].size = submit_cmd.size / 4;
|
||||
submit->cmd[i].iova = iova + submit_cmd.submit_offset;
|
||||
submit->cmd[i].idx = submit_cmd.submit_idx;
|
||||
|
||||
if (submit->valid)
|
||||
continue;
|
||||
|
@ -319,6 +319,101 @@ static void hangcheck_handler(unsigned long data)
|
||||
queue_work(priv->wq, &gpu->retire_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performance Counters:
|
||||
*/
|
||||
|
||||
/* called under perf_lock */
|
||||
static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs)
|
||||
{
|
||||
uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)];
|
||||
int i, n = min(ncntrs, gpu->num_perfcntrs);
|
||||
|
||||
/* read current values: */
|
||||
for (i = 0; i < gpu->num_perfcntrs; i++)
|
||||
current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg);
|
||||
|
||||
/* update cntrs: */
|
||||
for (i = 0; i < n; i++)
|
||||
cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i];
|
||||
|
||||
/* save current values: */
|
||||
for (i = 0; i < gpu->num_perfcntrs; i++)
|
||||
gpu->last_cntrs[i] = current_cntrs[i];
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void update_sw_cntrs(struct msm_gpu *gpu)
|
||||
{
|
||||
ktime_t time;
|
||||
uint32_t elapsed;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpu->perf_lock, flags);
|
||||
if (!gpu->perfcntr_active)
|
||||
goto out;
|
||||
|
||||
time = ktime_get();
|
||||
elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time));
|
||||
|
||||
gpu->totaltime += elapsed;
|
||||
if (gpu->last_sample.active)
|
||||
gpu->activetime += elapsed;
|
||||
|
||||
gpu->last_sample.active = msm_gpu_active(gpu);
|
||||
gpu->last_sample.time = time;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gpu->perf_lock, flags);
|
||||
}
|
||||
|
||||
void msm_gpu_perfcntr_start(struct msm_gpu *gpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpu->perf_lock, flags);
|
||||
/* we could dynamically enable/disable perfcntr registers too.. */
|
||||
gpu->last_sample.active = msm_gpu_active(gpu);
|
||||
gpu->last_sample.time = ktime_get();
|
||||
gpu->activetime = gpu->totaltime = 0;
|
||||
gpu->perfcntr_active = true;
|
||||
update_hw_cntrs(gpu, 0, NULL);
|
||||
spin_unlock_irqrestore(&gpu->perf_lock, flags);
|
||||
}
|
||||
|
||||
void msm_gpu_perfcntr_stop(struct msm_gpu *gpu)
|
||||
{
|
||||
gpu->perfcntr_active = false;
|
||||
}
|
||||
|
||||
/* returns -errno or # of cntrs sampled */
|
||||
int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
|
||||
uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&gpu->perf_lock, flags);
|
||||
|
||||
if (!gpu->perfcntr_active) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*activetime = gpu->activetime;
|
||||
*totaltime = gpu->totaltime;
|
||||
|
||||
gpu->activetime = gpu->totaltime = 0;
|
||||
|
||||
ret = update_hw_cntrs(gpu, ncntrs, cntrs);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gpu->perf_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cmdstream submission/retirement:
|
||||
*/
|
||||
@ -361,6 +456,7 @@ void msm_gpu_retire(struct msm_gpu *gpu)
|
||||
{
|
||||
struct msm_drm_private *priv = gpu->dev->dev_private;
|
||||
queue_work(priv->wq, &gpu->retire_work);
|
||||
update_sw_cntrs(gpu);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
|
||||
inactive_cancel(gpu);
|
||||
|
||||
msm_rd_dump_submit(submit);
|
||||
|
||||
gpu->submitted_fence = submit->fence;
|
||||
|
||||
update_sw_cntrs(gpu);
|
||||
|
||||
ret = gpu->funcs->submit(gpu, submit, ctx);
|
||||
priv->lastctx = ctx;
|
||||
|
||||
@ -429,6 +531,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
struct iommu_domain *iommu;
|
||||
int i, ret;
|
||||
|
||||
if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
|
||||
gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
|
||||
|
||||
gpu->dev = drm;
|
||||
gpu->funcs = funcs;
|
||||
gpu->name = name;
|
||||
@ -444,6 +549,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
|
||||
(unsigned long)gpu);
|
||||
|
||||
spin_lock_init(&gpu->perf_lock);
|
||||
|
||||
BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));
|
||||
|
||||
/* Map registers: */
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "msm_ringbuffer.h"
|
||||
|
||||
struct msm_gem_submit;
|
||||
struct msm_gpu_perfcntr;
|
||||
|
||||
/* So far, with hardware that I've seen to date, we can have:
|
||||
* + zero, one, or two z180 2d cores
|
||||
@ -64,6 +65,18 @@ struct msm_gpu {
|
||||
struct drm_device *dev;
|
||||
const struct msm_gpu_funcs *funcs;
|
||||
|
||||
/* performance counters (hw & sw): */
|
||||
spinlock_t perf_lock;
|
||||
bool perfcntr_active;
|
||||
struct {
|
||||
bool active;
|
||||
ktime_t time;
|
||||
} last_sample;
|
||||
uint32_t totaltime, activetime; /* sw counters */
|
||||
uint32_t last_cntrs[5]; /* hw counters */
|
||||
const struct msm_gpu_perfcntr *perfcntrs;
|
||||
uint32_t num_perfcntrs;
|
||||
|
||||
struct msm_ringbuffer *rb;
|
||||
uint32_t rb_iova;
|
||||
|
||||
@ -113,6 +126,19 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu)
|
||||
return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
|
||||
}
|
||||
|
||||
/* Perf-Counters:
|
||||
* The select_reg and select_val are just there for the benefit of the child
|
||||
* class that actually enables the perf counter.. but msm_gpu base class
|
||||
* will handle sampling/displaying the counters.
|
||||
*/
|
||||
|
||||
struct msm_gpu_perfcntr {
|
||||
uint32_t select_reg;
|
||||
uint32_t sample_reg;
|
||||
uint32_t select_val;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
|
||||
{
|
||||
msm_writel(data, gpu->mmio + (reg << 2));
|
||||
@ -126,6 +152,11 @@ static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)
|
||||
int msm_gpu_pm_suspend(struct msm_gpu *gpu);
|
||||
int msm_gpu_pm_resume(struct msm_gpu *gpu);
|
||||
|
||||
void msm_gpu_perfcntr_start(struct msm_gpu *gpu);
|
||||
void msm_gpu_perfcntr_stop(struct msm_gpu *gpu);
|
||||
int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
|
||||
uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs);
|
||||
|
||||
void msm_gpu_retire(struct msm_gpu *gpu);
|
||||
int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
struct msm_file_private *ctx);
|
||||
|
275
drivers/gpu/drm/msm/msm_perf.c
Normal file
275
drivers/gpu/drm/msm/msm_perf.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* For profiling, userspace can:
|
||||
*
|
||||
* tail -f /sys/kernel/debug/dri/<minor>/gpu
|
||||
*
|
||||
* This will enable performance counters/profiling to track the busy time
|
||||
* and any gpu specific performance counters that are supported.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gpu.h"
|
||||
|
||||
struct msm_perf_state {
|
||||
struct drm_device *dev;
|
||||
|
||||
bool open;
|
||||
int cnt;
|
||||
struct mutex read_lock;
|
||||
|
||||
char buf[256];
|
||||
int buftot, bufpos;
|
||||
|
||||
unsigned long next_jiffies;
|
||||
|
||||
struct dentry *ent;
|
||||
struct drm_info_node *node;
|
||||
};
|
||||
|
||||
#define SAMPLE_TIME (HZ/4)
|
||||
|
||||
/* wait for next sample time: */
|
||||
static int wait_sample(struct msm_perf_state *perf)
|
||||
{
|
||||
unsigned long start_jiffies = jiffies;
|
||||
|
||||
if (time_after(perf->next_jiffies, start_jiffies)) {
|
||||
unsigned long remaining_jiffies =
|
||||
perf->next_jiffies - start_jiffies;
|
||||
int ret = schedule_timeout_interruptible(remaining_jiffies);
|
||||
if (ret > 0) {
|
||||
/* interrupted */
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
}
|
||||
perf->next_jiffies += SAMPLE_TIME;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int refill_buf(struct msm_perf_state *perf)
|
||||
{
|
||||
struct msm_drm_private *priv = perf->dev->dev_private;
|
||||
struct msm_gpu *gpu = priv->gpu;
|
||||
char *ptr = perf->buf;
|
||||
int rem = sizeof(perf->buf);
|
||||
int i, n;
|
||||
|
||||
if ((perf->cnt++ % 32) == 0) {
|
||||
/* Header line: */
|
||||
n = snprintf(ptr, rem, "%%BUSY");
|
||||
ptr += n;
|
||||
rem -= n;
|
||||
|
||||
for (i = 0; i < gpu->num_perfcntrs; i++) {
|
||||
const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
|
||||
n = snprintf(ptr, rem, "\t%s", perfcntr->name);
|
||||
ptr += n;
|
||||
rem -= n;
|
||||
}
|
||||
} else {
|
||||
/* Sample line: */
|
||||
uint32_t activetime = 0, totaltime = 0;
|
||||
uint32_t cntrs[5];
|
||||
uint32_t val;
|
||||
int ret;
|
||||
|
||||
/* sleep until next sample time: */
|
||||
ret = wait_sample(perf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime,
|
||||
ARRAY_SIZE(cntrs), cntrs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = totaltime ? 1000 * activetime / totaltime : 0;
|
||||
n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10);
|
||||
ptr += n;
|
||||
rem -= n;
|
||||
|
||||
for (i = 0; i < ret; i++) {
|
||||
/* cycle counters (I think).. convert to MHz.. */
|
||||
val = cntrs[i] / 10000;
|
||||
n = snprintf(ptr, rem, "\t%5d.%02d",
|
||||
val / 100, val % 100);
|
||||
ptr += n;
|
||||
rem -= n;
|
||||
}
|
||||
}
|
||||
|
||||
n = snprintf(ptr, rem, "\n");
|
||||
ptr += n;
|
||||
rem -= n;
|
||||
|
||||
perf->bufpos = 0;
|
||||
perf->buftot = ptr - perf->buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t perf_read(struct file *file, char __user *buf,
|
||||
size_t sz, loff_t *ppos)
|
||||
{
|
||||
struct msm_perf_state *perf = file->private_data;
|
||||
int n = 0, ret;
|
||||
|
||||
mutex_lock(&perf->read_lock);
|
||||
|
||||
if (perf->bufpos >= perf->buftot) {
|
||||
ret = refill_buf(perf);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
n = min((int)sz, perf->buftot - perf->bufpos);
|
||||
ret = copy_to_user(buf, &perf->buf[perf->bufpos], n);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
perf->bufpos += n;
|
||||
*ppos += n;
|
||||
|
||||
out:
|
||||
mutex_unlock(&perf->read_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int perf_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct msm_perf_state *perf = inode->i_private;
|
||||
struct drm_device *dev = perf->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gpu *gpu = priv->gpu;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
if (perf->open || !gpu) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file->private_data = perf;
|
||||
perf->open = true;
|
||||
perf->cnt = 0;
|
||||
perf->buftot = 0;
|
||||
perf->bufpos = 0;
|
||||
msm_gpu_perfcntr_start(gpu);
|
||||
perf->next_jiffies = jiffies + SAMPLE_TIME;
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int perf_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct msm_perf_state *perf = inode->i_private;
|
||||
struct msm_drm_private *priv = perf->dev->dev_private;
|
||||
msm_gpu_perfcntr_stop(priv->gpu);
|
||||
perf->open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations perf_debugfs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = perf_open,
|
||||
.read = perf_read,
|
||||
.llseek = no_llseek,
|
||||
.release = perf_release,
|
||||
};
|
||||
|
||||
int msm_perf_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
struct msm_drm_private *priv = minor->dev->dev_private;
|
||||
struct msm_perf_state *perf;
|
||||
|
||||
/* only create on first minor: */
|
||||
if (priv->perf)
|
||||
return 0;
|
||||
|
||||
perf = kzalloc(sizeof(*perf), GFP_KERNEL);
|
||||
if (!perf)
|
||||
return -ENOMEM;
|
||||
|
||||
perf->dev = minor->dev;
|
||||
|
||||
mutex_init(&perf->read_lock);
|
||||
priv->perf = perf;
|
||||
|
||||
perf->node = kzalloc(sizeof(*perf->node), GFP_KERNEL);
|
||||
if (!perf->node)
|
||||
goto fail;
|
||||
|
||||
perf->ent = debugfs_create_file("perf", S_IFREG | S_IRUGO,
|
||||
minor->debugfs_root, perf, &perf_debugfs_fops);
|
||||
if (!perf->ent) {
|
||||
DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/perf\n",
|
||||
minor->debugfs_root->d_name.name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
perf->node->minor = minor;
|
||||
perf->node->dent = perf->ent;
|
||||
perf->node->info_ent = NULL;
|
||||
|
||||
mutex_lock(&minor->debugfs_lock);
|
||||
list_add(&perf->node->list, &minor->debugfs_list);
|
||||
mutex_unlock(&minor->debugfs_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
msm_perf_debugfs_cleanup(minor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void msm_perf_debugfs_cleanup(struct drm_minor *minor)
|
||||
{
|
||||
struct msm_drm_private *priv = minor->dev->dev_private;
|
||||
struct msm_perf_state *perf = priv->perf;
|
||||
|
||||
if (!perf)
|
||||
return;
|
||||
|
||||
priv->perf = NULL;
|
||||
|
||||
debugfs_remove(perf->ent);
|
||||
|
||||
if (perf->node) {
|
||||
mutex_lock(&minor->debugfs_lock);
|
||||
list_del(&perf->node->list);
|
||||
mutex_unlock(&minor->debugfs_lock);
|
||||
kfree(perf->node);
|
||||
}
|
||||
|
||||
mutex_destroy(&perf->read_lock);
|
||||
|
||||
kfree(perf);
|
||||
}
|
||||
|
||||
#endif
|
337
drivers/gpu/drm/msm/msm_rd.c
Normal file
337
drivers/gpu/drm/msm/msm_rd.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* For debugging crashes, userspace can:
|
||||
*
|
||||
* tail -f /sys/kernel/debug/dri/<minor>/rd > logfile.rd
|
||||
*
|
||||
* To log the cmdstream in a format that is understood by freedreno/cffdump
|
||||
* utility. By comparing the last successfully completed fence #, to the
|
||||
* cmdstream for the next fence, you can narrow down which process and submit
|
||||
* caused the gpu crash/lockup.
|
||||
*
|
||||
* This bypasses drm_debugfs_create_files() mainly because we need to use
|
||||
* our own fops for a bit more control. In particular, we don't want to
|
||||
* do anything if userspace doesn't have the debugfs file open.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gpu.h"
|
||||
#include "msm_gem.h"
|
||||
|
||||
enum rd_sect_type {
|
||||
RD_NONE,
|
||||
RD_TEST, /* ascii text */
|
||||
RD_CMD, /* ascii text */
|
||||
RD_GPUADDR, /* u32 gpuaddr, u32 size */
|
||||
RD_CONTEXT, /* raw dump */
|
||||
RD_CMDSTREAM, /* raw dump */
|
||||
RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
|
||||
RD_PARAM, /* u32 param_type, u32 param_val, u32 bitlen */
|
||||
RD_FLUSH, /* empty, clear previous params */
|
||||
RD_PROGRAM, /* shader program, raw dump */
|
||||
RD_VERT_SHADER,
|
||||
RD_FRAG_SHADER,
|
||||
RD_BUFFER_CONTENTS,
|
||||
RD_GPU_ID,
|
||||
};
|
||||
|
||||
#define BUF_SZ 512 /* should be power of 2 */
|
||||
|
||||
/* space used: */
|
||||
#define circ_count(circ) \
|
||||
(CIRC_CNT((circ)->head, (circ)->tail, BUF_SZ))
|
||||
#define circ_count_to_end(circ) \
|
||||
(CIRC_CNT_TO_END((circ)->head, (circ)->tail, BUF_SZ))
|
||||
/* space available: */
|
||||
#define circ_space(circ) \
|
||||
(CIRC_SPACE((circ)->head, (circ)->tail, BUF_SZ))
|
||||
#define circ_space_to_end(circ) \
|
||||
(CIRC_SPACE_TO_END((circ)->head, (circ)->tail, BUF_SZ))
|
||||
|
||||
struct msm_rd_state {
|
||||
struct drm_device *dev;
|
||||
|
||||
bool open;
|
||||
|
||||
struct dentry *ent;
|
||||
struct drm_info_node *node;
|
||||
|
||||
/* current submit to read out: */
|
||||
struct msm_gem_submit *submit;
|
||||
|
||||
/* fifo access is synchronized on the producer side by
|
||||
* struct_mutex held by submit code (otherwise we could
|
||||
* end up w/ cmds logged in different order than they
|
||||
* were executed). And read_lock synchronizes the reads
|
||||
*/
|
||||
struct mutex read_lock;
|
||||
|
||||
wait_queue_head_t fifo_event;
|
||||
struct circ_buf fifo;
|
||||
|
||||
char buf[BUF_SZ];
|
||||
};
|
||||
|
||||
static void rd_write(struct msm_rd_state *rd, const void *buf, int sz)
|
||||
{
|
||||
struct circ_buf *fifo = &rd->fifo;
|
||||
const char *ptr = buf;
|
||||
|
||||
while (sz > 0) {
|
||||
char *fptr = &fifo->buf[fifo->head];
|
||||
int n;
|
||||
|
||||
wait_event(rd->fifo_event, circ_space(&rd->fifo) > 0);
|
||||
|
||||
n = min(sz, circ_space_to_end(&rd->fifo));
|
||||
memcpy(fptr, ptr, n);
|
||||
|
||||
fifo->head = (fifo->head + n) & (BUF_SZ - 1);
|
||||
sz -= n;
|
||||
ptr += n;
|
||||
|
||||
wake_up_all(&rd->fifo_event);
|
||||
}
|
||||
}
|
||||
|
||||
static void rd_write_section(struct msm_rd_state *rd,
|
||||
enum rd_sect_type type, const void *buf, int sz)
|
||||
{
|
||||
rd_write(rd, &type, 4);
|
||||
rd_write(rd, &sz, 4);
|
||||
rd_write(rd, buf, sz);
|
||||
}
|
||||
|
||||
static ssize_t rd_read(struct file *file, char __user *buf,
|
||||
size_t sz, loff_t *ppos)
|
||||
{
|
||||
struct msm_rd_state *rd = file->private_data;
|
||||
struct circ_buf *fifo = &rd->fifo;
|
||||
const char *fptr = &fifo->buf[fifo->tail];
|
||||
int n = 0, ret = 0;
|
||||
|
||||
mutex_lock(&rd->read_lock);
|
||||
|
||||
ret = wait_event_interruptible(rd->fifo_event,
|
||||
circ_count(&rd->fifo) > 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
n = min_t(int, sz, circ_count_to_end(&rd->fifo));
|
||||
ret = copy_to_user(buf, fptr, n);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
fifo->tail = (fifo->tail + n) & (BUF_SZ - 1);
|
||||
*ppos += n;
|
||||
|
||||
wake_up_all(&rd->fifo_event);
|
||||
|
||||
out:
|
||||
mutex_unlock(&rd->read_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int rd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct msm_rd_state *rd = inode->i_private;
|
||||
struct drm_device *dev = rd->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gpu *gpu = priv->gpu;
|
||||
uint64_t val;
|
||||
uint32_t gpu_id;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
if (rd->open || !gpu) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file->private_data = rd;
|
||||
rd->open = true;
|
||||
|
||||
/* the parsing tools need to know gpu-id to know which
|
||||
* register database to load.
|
||||
*/
|
||||
gpu->funcs->get_param(gpu, MSM_PARAM_GPU_ID, &val);
|
||||
gpu_id = val;
|
||||
|
||||
rd_write_section(rd, RD_GPU_ID, &gpu_id, sizeof(gpu_id));
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct msm_rd_state *rd = inode->i_private;
|
||||
rd->open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations rd_debugfs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rd_open,
|
||||
.read = rd_read,
|
||||
.llseek = no_llseek,
|
||||
.release = rd_release,
|
||||
};
|
||||
|
||||
int msm_rd_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
struct msm_drm_private *priv = minor->dev->dev_private;
|
||||
struct msm_rd_state *rd;
|
||||
|
||||
/* only create on first minor: */
|
||||
if (priv->rd)
|
||||
return 0;
|
||||
|
||||
rd = kzalloc(sizeof(*rd), GFP_KERNEL);
|
||||
if (!rd)
|
||||
return -ENOMEM;
|
||||
|
||||
rd->dev = minor->dev;
|
||||
rd->fifo.buf = rd->buf;
|
||||
|
||||
mutex_init(&rd->read_lock);
|
||||
priv->rd = rd;
|
||||
|
||||
init_waitqueue_head(&rd->fifo_event);
|
||||
|
||||
rd->node = kzalloc(sizeof(*rd->node), GFP_KERNEL);
|
||||
if (!rd->node)
|
||||
goto fail;
|
||||
|
||||
rd->ent = debugfs_create_file("rd", S_IFREG | S_IRUGO,
|
||||
minor->debugfs_root, rd, &rd_debugfs_fops);
|
||||
if (!rd->ent) {
|
||||
DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/rd\n",
|
||||
minor->debugfs_root->d_name.name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rd->node->minor = minor;
|
||||
rd->node->dent = rd->ent;
|
||||
rd->node->info_ent = NULL;
|
||||
|
||||
mutex_lock(&minor->debugfs_lock);
|
||||
list_add(&rd->node->list, &minor->debugfs_list);
|
||||
mutex_unlock(&minor->debugfs_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
msm_rd_debugfs_cleanup(minor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void msm_rd_debugfs_cleanup(struct drm_minor *minor)
|
||||
{
|
||||
struct msm_drm_private *priv = minor->dev->dev_private;
|
||||
struct msm_rd_state *rd = priv->rd;
|
||||
|
||||
if (!rd)
|
||||
return;
|
||||
|
||||
priv->rd = NULL;
|
||||
|
||||
debugfs_remove(rd->ent);
|
||||
|
||||
if (rd->node) {
|
||||
mutex_lock(&minor->debugfs_lock);
|
||||
list_del(&rd->node->list);
|
||||
mutex_unlock(&minor->debugfs_lock);
|
||||
kfree(rd->node);
|
||||
}
|
||||
|
||||
mutex_destroy(&rd->read_lock);
|
||||
|
||||
kfree(rd);
|
||||
}
|
||||
|
||||
/* called under struct_mutex */
|
||||
void msm_rd_dump_submit(struct msm_gem_submit *submit)
|
||||
{
|
||||
struct drm_device *dev = submit->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_rd_state *rd = priv->rd;
|
||||
char msg[128];
|
||||
int i, n;
|
||||
|
||||
if (!rd->open)
|
||||
return;
|
||||
|
||||
/* writing into fifo is serialized by caller, and
|
||||
* rd->read_lock is used to serialize the reads
|
||||
*/
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
|
||||
n = snprintf(msg, sizeof(msg), "%.*s/%d: fence=%u",
|
||||
TASK_COMM_LEN, current->comm, task_pid_nr(current),
|
||||
submit->fence);
|
||||
|
||||
rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
|
||||
|
||||
/* could be nice to have an option (module-param?) to snapshot
|
||||
* all the bo's associated with the submit. Handy to see vtx
|
||||
* buffers, etc. For now just the cmdstream bo's is enough.
|
||||
*/
|
||||
|
||||
for (i = 0; i < submit->nr_cmds; i++) {
|
||||
uint32_t idx = submit->cmd[i].idx;
|
||||
uint32_t iova = submit->cmd[i].iova;
|
||||
uint32_t szd = submit->cmd[i].size; /* in dwords */
|
||||
struct msm_gem_object *obj = submit->bos[idx].obj;
|
||||
const char *buf = msm_gem_vaddr_locked(&obj->base);
|
||||
|
||||
buf += iova - submit->bos[idx].iova;
|
||||
|
||||
rd_write_section(rd, RD_GPUADDR,
|
||||
(uint32_t[2]){ iova, szd * 4 }, 8);
|
||||
rd_write_section(rd, RD_BUFFER_CONTENTS,
|
||||
buf, szd * 4);
|
||||
|
||||
switch (submit->cmd[i].type) {
|
||||
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
|
||||
/* ignore IB-targets, we've logged the buffer, the
|
||||
* parser tool will follow the IB based on the logged
|
||||
* buffer/gpuaddr, so nothing more to do.
|
||||
*/
|
||||
break;
|
||||
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
|
||||
case MSM_SUBMIT_CMD_BUF:
|
||||
rd_write_section(rd, RD_CMDSTREAM_ADDR,
|
||||
(uint32_t[2]){ iova, szd }, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user