From bb0f1b5c1695b4399cfd2359c114ae63edbb3ad8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 21:09:27 +0100 Subject: drm: pass the irq explicitly to drm_irq_install Unfortunately this requires a drm-wide change, and I didn't see a sane way around that. Luckily it's fairly simple, we just need to inline the respective get_irq implementation from either drm_pci.c or drm_platform.c. With that we can now also remove drm_dev_to_irq from drm_irq.c. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/msm/msm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/msm') diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index f9de156b9e65..50ec1bed5820 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -288,7 +288,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) } pm_runtime_get_sync(dev->dev); - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); pm_runtime_put_sync(dev->dev); if (ret < 0) { dev_err(dev->dev, "failed to install IRQ handler\n"); -- cgit v1.2.3 From 8a57e95057acdc37362f09ede3d7520f71de6f93 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 25 Apr 2014 12:30:53 -0400 Subject: drm/msm/mdp5: fix crash in error/unload paths Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/msm') 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) 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); -- cgit v1.2.3 From 3189650d7f5de9e1b7f530464de7f0000411a408 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 19 May 2014 13:53:20 -0400 Subject: drm/msm/hdmi: use gpio and HPD polling The hotplug detect and irq does not seem to be reliable on all devices for some reason. For now it is more reliable to use polling, and give preference to raw gpio status if it disagrees with the debounced hpd status. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/hdmi/hdmi_connector.c | 52 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/msm') 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) } } +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; -- cgit v1.2.3 From fb27b8f29f60b8799df1aa60cd9647d56d62810e Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 30 May 2014 15:37:54 -0400 Subject: drm/msm: update for ARCH_MSM -> ARCH_QCOM Architecture rename/split.. ARCH_QCOM is for the non-legacy platforms (ie. device-tree, multiplatform support, etc). Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Kconfig | 2 +- drivers/gpu/drm/msm/msm_drv.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/msm') 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 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 diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 9d10ee0b5aac..588c427d0243 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -33,7 +33,7 @@ #include -#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) { -- cgit v1.2.3 From a7d3c9509b2fecf8e593f3c933ab302cbe987d2e Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 30 May 2014 14:47:38 -0400 Subject: drm/msm: add rd logging debugfs To ease debugging, add debugfs file which can be cat/tail'd to log submits, along with fence #. If GPU hangs, you can look at 'gpu' debugfs file to find last completed fence and current register state, and compare with logged rd file to narrow down the DRAW_INDX which triggered the GPU hang. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/msm_drv.c | 38 +++- drivers/gpu/drm/msm/msm_drv.h | 11 ++ drivers/gpu/drm/msm/msm_gem.h | 1 + drivers/gpu/drm/msm/msm_gem_submit.c | 1 + drivers/gpu/drm/msm/msm_gpu.c | 4 + drivers/gpu/drm/msm/msm_rd.c | 337 +++++++++++++++++++++++++++++++++++ 7 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/msm/msm_rd.c (limited to 'drivers/gpu/drm/msm') diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 5e1e6b0cd8ac..9a506b5921e5 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -34,6 +34,7 @@ msm-y := \ msm_gem_submit.o \ msm_gpu.o \ msm_iommu.o \ + msm_rd.o \ msm_ringbuffer.o msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 50ec1bed5820..929f57343440 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -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,35 @@ 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; + } + + 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 +578,16 @@ 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); } #endif diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 588c427d0243..c9c71a0c3e6f 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -55,6 +55,8 @@ 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_gem_submit; #define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */ @@ -82,6 +84,8 @@ struct msm_drm_private { uint32_t next_fence, completed_fence; wait_queue_head_t fence_event; + struct msm_rd_state *rd; + /* list of GEM objects: */ struct list_head inactive_list; @@ -204,6 +208,13 @@ 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); +#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, 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 { 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; 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, 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; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 3e667ca1f2b9..67371f3ddf99 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -377,6 +377,10 @@ 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; + ret = gpu->funcs->submit(gpu, submit, ctx); priv->lastctx = ctx; 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 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * 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 . + */ + +/* For debugging crashes, userspace can: + * + * tail -f /sys/kernel/debug/dri//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 +#include +#include +#include + +#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 -- cgit v1.2.3 From 70c70f091b1ffd16b3e1a439bd595f7d539b1d5d Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 30 May 2014 14:49:43 -0400 Subject: drm/msm: add perf logging debugfs Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 20 ++- drivers/gpu/drm/msm/msm_drv.c | 7 + drivers/gpu/drm/msm/msm_drv.h | 4 + drivers/gpu/drm/msm/msm_gpu.c | 103 +++++++++++++ drivers/gpu/drm/msm/msm_gpu.h | 31 ++++ drivers/gpu/drm/msm/msm_perf.c | 275 ++++++++++++++++++++++++++++++++++ 7 files changed, 436 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_perf.c (limited to 'drivers/gpu/drm/msm') diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 9a506b5921e5..93ca49c8df44 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -34,6 +34,7 @@ msm-y := \ msm_gem_submit.o \ msm_gpu.o \ msm_iommu.o \ + msm_perf.o \ msm_rd.o \ msm_ringbuffer.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) /* 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; diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 929f57343440..1d5151ab81e7 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -548,6 +548,12 @@ static int late_init_minor(struct drm_minor *minor) 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; } @@ -588,6 +594,7 @@ static void msm_debugfs_cleanup(struct drm_minor *minor) if (!minor->dev->dev_private) return; msm_rd_debugfs_cleanup(minor); + msm_perf_debugfs_cleanup(minor); } #endif diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index c9c71a0c3e6f..8a2c5fd0893e 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -56,6 +56,7 @@ 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 (?) */ @@ -85,6 +86,7 @@ struct msm_drm_private { 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; @@ -212,6 +214,8 @@ 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) {} diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 67371f3ddf99..c6322197db8c 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -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: */ @@ -381,6 +477,8 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, gpu->submitted_fence = submit->fence; + update_sw_cntrs(gpu); + ret = gpu->funcs->submit(gpu, submit, ctx); priv->lastctx = ctx; @@ -433,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; @@ -448,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: */ 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 @@ #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); 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 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * 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 . + */ + +/* For profiling, userspace can: + * + * tail -f /sys/kernel/debug/dri//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 + +#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 -- cgit v1.2.3 From 814cb96fdc91495e13be7569dca43b7c1043ad2a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 2 Jun 2014 07:22:29 -0400 Subject: drm/msm: remove redundant private plane cleanup Now that drm core knows about private planes, it cleans them up for us. Trying to do this twice results in badness. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 2 -- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers/gpu/drm/msm') 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) { 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); 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) { 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); -- cgit v1.2.3 From a0906a023b02366b57eae71c5b9b542d9dd51e4c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 2 Jun 2014 07:24:27 -0400 Subject: drm/msm/mdp5: fix error return value Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/msm') 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) 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; -- cgit v1.2.3 From 543d3011f1c193ba2257a754757117cafda2252b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 2 Jun 2014 07:25:56 -0400 Subject: drm/msm: use correct gfp flag for vram allocation We want at least __GFP_WAIT, otherwise dma-mapping tries to use coherent pool rather than CMA pool. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/msm') diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 1d5151ab81e7..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) * 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; -- cgit v1.2.3 From 5ea1f752ae04be403a3dc8ec876a60d7f5f6990a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 30 May 2014 12:29:48 -0400 Subject: drm: add drm_fb_helper_restore_fbdev_mode_unlocked() All drm_fb_helper_restore_fbdev_mode() call sites, save one, do the same locking. Simplify this into drm_fb_helper_restore_fbdev_mode_unlocked(). Signed-off-by: Rob Clark Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/armada/armada_fbdev.c | 4 +-- drivers/gpu/drm/drm_fb_cma_helper.c | 9 ++---- drivers/gpu/drm/drm_fb_helper.c | 50 ++++++++++++++++++++++--------- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 +-- drivers/gpu/drm/gma500/psb_drv.c | 4 +-- drivers/gpu/drm/i915/intel_fbdev.c | 6 +--- drivers/gpu/drm/msm/msm_drv.c | 7 ++--- drivers/gpu/drm/omapdrm/omap_drv.c | 4 +-- drivers/gpu/drm/tegra/fb.c | 7 ++--- include/drm/drm_fb_helper.h | 2 +- 10 files changed, 48 insertions(+), 49 deletions(-) (limited to 'drivers/gpu/drm/msm') diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 948cb14c561e..fd166f532ab9 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev) { struct armada_private *priv = dev->dev_private; - drm_modeset_lock_all(dev); if (priv->fbdev) - drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); } void armada_fbdev_fini(struct drm_device *dev) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 61b5a47ad239..f27c883be391 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { - if (fbdev_cma) { - struct drm_device *dev = fbdev_cma->fb_helper.dev; - - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); - drm_modeset_unlock_all(dev); - } + if (fbdev_cma) + drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 43329cee299f..d5d8cea1a679 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -273,15 +273,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); -/** - * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration - * @fb_helper: fbcon to restore - * - * This should be called from driver's drm ->lastclose callback - * when implementing an fbcon on top of kms using this helper. This ensures that - * the user isn't greeted with a black screen when e.g. X dies. - */ -bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) +static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; @@ -311,7 +303,40 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } return error; } -EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); +/** + * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. + * + * Use this variant if you need to bypass locking (panic), or already + * hold all modeset locks. Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked() + */ +static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) +{ + return restore_fbdev_mode(fb_helper); +} + +/** + * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. + */ +bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + bool ret; + drm_modeset_lock_all(dev); + ret = restore_fbdev_mode(fb_helper); + drm_modeset_unlock_all(dev); + return ret; +} +EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); /* * restore fbcon display for all kms driver's using this helper, used for sysrq @@ -824,7 +849,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var); int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; if (var->pixclock != 0) { @@ -832,9 +856,7 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; } - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(fb_helper); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 14bbaa33b477..d771b467cf0c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -367,7 +367,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return; - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(private->fb_helper); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper); } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0a3101a3db19..59ea45e5c97e 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_fbdev *fbdev = dev_priv->fbdev; - drm_modeset_lock_all(dev); - ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - drm_modeset_unlock_all(dev); return; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index e2d416149f53..6ea2d75464da 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -687,11 +687,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev) if (!dev_priv->fbdev) return; - drm_modeset_lock_all(dev); - - ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); + ret = drm_fb_helper_restore_fbdev_mode_unlocked(&dev_priv->fbdev->helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - - drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index c071aacf2752..0d2562fb681e 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -386,11 +386,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file) static void msm_lastclose(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; - if (priv->fbdev) { - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); - } + if (priv->fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); } static irqreturn_t msm_irq(int irq, void *arg) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index c8270e4b26f3..002b9721e85a 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -588,9 +588,7 @@ static void dev_lastclose(struct drm_device *dev) } } - drm_modeset_lock_all(dev); - ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); + ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); if (ret) DBG("failed to restore crtc mode"); } diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f7fca09d4921..9798a7080322 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev) void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) { - if (fbdev) { - drm_modeset_lock_all(fbdev->base.dev); - drm_fb_helper_restore_fbdev_mode(&fbdev->base); - drm_modeset_unlock_all(fbdev->base.dev); - } + if (fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base); } static void tegra_fb_output_poll_changed(struct drm_device *drm) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6e622f7d481d..7997246d4039 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -108,7 +108,7 @@ int drm_fb_helper_set_par(struct fb_info *info); int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info); -bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper); +bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper); void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, -- cgit v1.2.3