aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/msm_drv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/msm_drv.c')
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c383
1 files changed, 320 insertions, 63 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index b3a2f162904..9a5d87db5c2 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -17,6 +17,7 @@
#include "msm_drv.h"
#include "msm_gpu.h"
+#include "msm_kms.h"
static void msm_fb_output_poll_changed(struct drm_device *dev)
{
@@ -30,50 +31,19 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
.output_poll_changed = msm_fb_output_poll_changed,
};
-static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
- unsigned long iova, int flags, void *arg)
-{
- DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
- return 0;
-}
-
-int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu)
+int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu)
{
struct msm_drm_private *priv = dev->dev_private;
- int idx = priv->num_iommus++;
+ int idx = priv->num_mmus++;
- if (WARN_ON(idx >= ARRAY_SIZE(priv->iommus)))
+ if (WARN_ON(idx >= ARRAY_SIZE(priv->mmus)))
return -EINVAL;
- priv->iommus[idx] = iommu;
-
- iommu_set_fault_handler(iommu, msm_fault_handler, dev);
-
- /* need to iommu_attach_device() somewhere?? on resume?? */
+ priv->mmus[idx] = mmu;
return idx;
}
-int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
- const char **names, int cnt)
-{
- int i, ret;
-
- for (i = 0; i < cnt; i++) {
- /* TODO maybe some day msm iommu won't require this hack: */
- struct device *msm_iommu_get_ctx(const char *ctx_name);
- struct device *ctx = msm_iommu_get_ctx(names[i]);
- if (!ctx)
- continue;
- ret = iommu_attach_device(iommu, ctx);
- if (ret) {
- dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
- return ret;
- }
- }
- return 0;
-}
-
#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
static bool reglog = false;
MODULE_PARM_DESC(reglog, "Enable register read/write logging");
@@ -82,6 +52,14 @@ module_param(reglog, bool, 0600);
#define reglog 0
#endif
+static char *vram;
+MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU");
+module_param(vram, charp, 0);
+
+/*
+ * Util/helpers:
+ */
+
void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
const char *dbgname)
{
@@ -161,6 +139,16 @@ static int msm_unload(struct drm_device *dev)
mutex_unlock(&dev->struct_mutex);
}
+ if (priv->vram.paddr) {
+ DEFINE_DMA_ATTRS(attrs);
+ dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+ drm_mm_takedown(&priv->vram.mm);
+ dma_free_attrs(dev->dev, priv->vram.size, NULL,
+ priv->vram.paddr, &attrs);
+ }
+
+ component_unbind_all(dev->dev, dev);
+
dev->dev_private = NULL;
kfree(priv);
@@ -168,6 +156,24 @@ static int msm_unload(struct drm_device *dev)
return 0;
}
+static int get_mdp_ver(struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+ static const struct of_device_id match_types[] = { {
+ .compatible = "qcom,mdss_mdp",
+ .data = (void *)5,
+ }, {
+ /* end node */
+ } };
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ match = of_match_node(match_types, dev->of_node);
+ if (match)
+ return (int)match->data;
+#endif
+ return 4;
+}
+
static int msm_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
@@ -175,6 +181,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
struct msm_kms *kms;
int ret;
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(dev->dev, "failed to allocate private data\n");
@@ -187,10 +194,64 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
init_waitqueue_head(&priv->fence_event);
INIT_LIST_HEAD(&priv->inactive_list);
+ INIT_LIST_HEAD(&priv->fence_cbs);
drm_mode_config_init(dev);
- kms = mdp4_kms_init(dev);
+ /* if we have no IOMMU, then we need to use carveout allocator.
+ * Grab the entire CMA chunk carved out in early startup in
+ * mach-msm:
+ */
+ if (!iommu_present(&platform_bus_type)) {
+ DEFINE_DMA_ATTRS(attrs);
+ unsigned long size;
+ void *p;
+
+ DBG("using %s VRAM carveout", vram);
+ size = memparse(vram, NULL);
+ priv->vram.size = size;
+
+ drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1);
+
+ dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+ dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+
+ /* note that for no-kernel-mapping, the vaddr returned
+ * is bogus, but non-null if allocation succeeded:
+ */
+ p = dma_alloc_attrs(dev->dev, size,
+ &priv->vram.paddr, GFP_KERNEL, &attrs);
+ if (!p) {
+ dev_err(dev->dev, "failed to allocate VRAM\n");
+ priv->vram.paddr = 0;
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ dev_info(dev->dev, "VRAM: %08x->%08x\n",
+ (uint32_t)priv->vram.paddr,
+ (uint32_t)(priv->vram.paddr + size));
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ /* Bind all our sub-components: */
+ ret = component_bind_all(dev->dev, dev);
+ if (ret)
+ return ret;
+
+ switch (get_mdp_ver(pdev)) {
+ case 4:
+ kms = mdp4_kms_init(dev);
+ break;
+ case 5:
+ kms = mdp5_kms_init(dev);
+ break;
+ default:
+ kms = ERR_PTR(-ENODEV);
+ break;
+ }
+
if (IS_ERR(kms)) {
/*
* NOTE: once we have GPU support, having no kms should not
@@ -227,19 +288,21 @@ 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");
goto fail;
}
- platform_set_drvdata(pdev, dev);
-
#ifdef CONFIG_DRM_MSM_FBDEV
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;
@@ -264,7 +327,6 @@ static void load_gpu(struct drm_device *dev)
gpu = NULL;
/* not fatal */
}
- mutex_unlock(&dev->struct_mutex);
if (gpu) {
int ret;
@@ -274,10 +336,16 @@ static void load_gpu(struct drm_device *dev)
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
gpu->funcs->destroy(gpu);
gpu = NULL;
+ } else {
+ /* give inactive pm a chance to kick in: */
+ msm_gpu_retire(gpu);
}
+
}
priv->gpu = gpu;
+
+ mutex_unlock(&dev->struct_mutex);
}
static int msm_open(struct drm_device *dev, struct drm_file *file)
@@ -318,14 +386,11 @@ 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(DRM_IRQ_ARGS)
+static irqreturn_t msm_irq(int irq, void *arg)
{
struct drm_device *dev = arg;
struct msm_drm_private *priv = dev->dev_private;
@@ -414,7 +479,7 @@ static int msm_gem_show(struct drm_device *dev, struct seq_file *m)
static int msm_mm_show(struct drm_device *dev, struct seq_file *m)
{
- return drm_mm_dump_table(m, dev->mm_private);
+ return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm);
}
static int msm_fb_show(struct drm_device *dev, struct seq_file *m)
@@ -467,6 +532,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;
@@ -481,13 +581,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
@@ -539,15 +643,36 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
return ret;
}
-/* call under struct_mutex */
+/* called from workqueue */
void msm_update_fence(struct drm_device *dev, uint32_t fence)
{
struct msm_drm_private *priv = dev->dev_private;
- if (fence > priv->completed_fence) {
- priv->completed_fence = fence;
- wake_up_all(&priv->fence_event);
+ mutex_lock(&dev->struct_mutex);
+ priv->completed_fence = max(fence, priv->completed_fence);
+
+ while (!list_empty(&priv->fence_cbs)) {
+ struct msm_fence_cb *cb;
+
+ cb = list_first_entry(&priv->fence_cbs,
+ struct msm_fence_cb, work.entry);
+
+ if (cb->fence > priv->completed_fence)
+ break;
+
+ list_del_init(&cb->work.entry);
+ queue_work(priv->wq, &cb->work);
}
+
+ mutex_unlock(&dev->struct_mutex);
+
+ wake_up_all(&priv->fence_event);
+}
+
+void __msm_fence_worker(struct work_struct *work)
+{
+ struct msm_fence_cb *cb = container_of(work, struct msm_fence_cb, work);
+ cb->func(cb);
}
/*
@@ -579,6 +704,12 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_msm_gem_new *args = data;
+
+ if (args->flags & ~MSM_BO_FLAGS) {
+ DRM_ERROR("invalid flags: %08x\n", args->flags);
+ return -EINVAL;
+ }
+
return msm_gem_new_handle(dev, file, args->size,
args->flags, &args->handle);
}
@@ -592,6 +723,11 @@ static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
struct drm_gem_object *obj;
int ret;
+ if (args->op & ~MSM_PREP_FLAGS) {
+ DRM_ERROR("invalid op: %08x\n", args->op);
+ return -EINVAL;
+ }
+
obj = drm_gem_object_lookup(dev, file, args->handle);
if (!obj)
return -ENOENT;
@@ -646,17 +782,24 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_msm_wait_fence *args = data;
- return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout));
+
+ if (args->pad) {
+ DRM_ERROR("invalid pad: %08x\n", args->pad);
+ return -EINVAL;
+ }
+
+ return msm_wait_fence_interruptable(dev, args->fence,
+ &TS(args->timeout));
}
static const struct drm_ioctl_desc msm_ioctls[] = {
- DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_UNLOCKED|DRM_AUTH),
- DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH),
- DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH),
- DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
- DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
- DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_UNLOCKED|DRM_AUTH),
- DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct vm_operations_struct vm_ops = {
@@ -680,7 +823,11 @@ static const struct file_operations fops = {
};
static struct drm_driver msm_driver = {
- .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+ .driver_features = DRIVER_HAVE_IRQ |
+ DRIVER_GEM |
+ DRIVER_PRIME |
+ DRIVER_RENDER |
+ DRIVER_MODESET,
.load = msm_load,
.unload = msm_unload,
.open = msm_open,
@@ -698,6 +845,16 @@ static struct drm_driver msm_driver = {
.dumb_create = msm_gem_dumb_create,
.dumb_map_offset = msm_gem_dumb_map_offset,
.dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_pin = msm_gem_prime_pin,
+ .gem_prime_unpin = msm_gem_prime_unpin,
+ .gem_prime_get_sg_table = msm_gem_prime_get_sg_table,
+ .gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
+ .gem_prime_vmap = msm_gem_prime_vmap,
+ .gem_prime_vunmap = msm_gem_prime_vunmap,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = msm_debugfs_init,
.debugfs_cleanup = msm_debugfs_cleanup,
@@ -737,17 +894,110 @@ static const struct dev_pm_ops msm_pm_ops = {
};
/*
+ * Componentized driver support:
+ */
+
+#ifdef CONFIG_OF
+/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx
+ * (or probably any other).. so probably some room for some helpers
+ */
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int msm_drm_add_components(struct device *master, struct master *m)
+{
+ struct device_node *np = master->of_node;
+ unsigned i;
+ int ret;
+
+ for (i = 0; ; i++) {
+ struct device_node *node;
+
+ node = of_parse_phandle(np, "connectors", i);
+ if (!node)
+ break;
+
+ ret = component_master_add_child(m, compare_of, node);
+ of_node_put(node);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+#else
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == data;
+}
+
+static int msm_drm_add_components(struct device *master, struct master *m)
+{
+ /* For non-DT case, it kinda sucks. We don't actually have a way
+ * to know whether or not we are waiting for certain devices (or if
+ * they are simply not present). But for non-DT we only need to
+ * care about apq8064/apq8060/etc (all mdp4/a3xx):
+ */
+ static const char *devnames[] = {
+ "hdmi_msm.0", "kgsl-3d0.0",
+ };
+ int i;
+
+ DBG("Adding components..");
+
+ for (i = 0; i < ARRAY_SIZE(devnames); i++) {
+ struct device *dev;
+ int ret;
+
+ dev = bus_find_device_by_name(&platform_bus_type,
+ NULL, devnames[i]);
+ if (!dev) {
+ dev_info(master, "still waiting for %s\n", devnames[i]);
+ return -EPROBE_DEFER;
+ }
+
+ ret = component_master_add_child(m, compare_dev, dev);
+ if (ret) {
+ DBG("could not add child: %d", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static int msm_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&msm_driver, to_platform_device(dev));
+}
+
+static void msm_drm_unbind(struct device *dev)
+{
+ drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
+}
+
+static const struct component_master_ops msm_drm_ops = {
+ .add_components = msm_drm_add_components,
+ .bind = msm_drm_bind,
+ .unbind = msm_drm_unbind,
+};
+
+/*
* Platform driver:
*/
static int msm_pdev_probe(struct platform_device *pdev)
{
- return drm_platform_init(&msm_driver, pdev);
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ return component_master_add(&pdev->dev, &msm_drm_ops);
}
static int msm_pdev_remove(struct platform_device *pdev)
{
- drm_platform_exit(&msm_driver, pdev);
+ component_master_del(&pdev->dev, &msm_drm_ops);
return 0;
}
@@ -757,12 +1007,19 @@ static const struct platform_device_id msm_id[] = {
{ }
};
+static const struct of_device_id dt_match[] = {
+ { .compatible = "qcom,mdss_mdp" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
static struct platform_driver msm_platform_driver = {
.probe = msm_pdev_probe,
.remove = msm_pdev_remove,
.driver = {
.owner = THIS_MODULE,
.name = "msm",
+ .of_match_table = dt_match,
.pm = &msm_pm_ops,
},
.id_table = msm_id,