drm/rockchip: vop: add vop full series of vop support
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_drv.c
index f22e1e1ee64aae52a1e3efb657a78790bd6ba573..d0cb8b62afea7376b7451b18130629c5e12b086d 100644 (file)
  * GNU General Public License for more details.
  */
 
-#include <asm/dma-iommu.h>
+#include <linux/dma-iommu.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_sync_helper.h>
+#include <drm/rockchip_drm.h>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <linux/module.h>
 #include <linux/of_graph.h>
 #include <linux/component.h>
+#include <linux/fence.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_fbdev.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_rga.h"
 
 #define DRIVER_NAME    "rockchip"
 #define DRIVER_DESC    "RockChip Soc DRM"
@@ -36,6 +40,9 @@
 #define DRIVER_MAJOR   1
 #define DRIVER_MINOR   0
 
+static LIST_HEAD(rockchip_drm_subdrv_list);
+static DEFINE_MUTEX(subdrv_list_mutex);
+
 /*
  * Attach a (component) device to the shared drm dma mapping from master drm
  * device.  This is used by the VOPs to map GEM buffers to a common DMA
@@ -44,7 +51,8 @@
 int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
                                   struct device *dev)
 {
-       struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
+       struct rockchip_drm_private *private = drm_dev->dev_private;
+       struct iommu_domain *domain = private->domain;
        int ret;
 
        ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
@@ -52,23 +60,35 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
                return ret;
 
        dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+       ret = iommu_attach_device(domain, dev);
+       if (ret) {
+               dev_err(dev, "Failed to attach iommu device\n");
+               return ret;
+       }
 
-       return arm_iommu_attach_device(dev, mapping);
+       if (!common_iommu_setup_dma_ops(dev, 0x10000000, SZ_2G, domain->ops)) {
+               dev_err(dev, "Failed to set dma_ops\n");
+               iommu_detach_device(domain, dev);
+               ret = -ENODEV;
+       }
+
+       return ret;
 }
-EXPORT_SYMBOL_GPL(rockchip_drm_dma_attach_device);
 
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
                                    struct device *dev)
 {
-       arm_iommu_detach_device(dev);
+       struct rockchip_drm_private *private = drm_dev->dev_private;
+       struct iommu_domain *domain = private->domain;
+
+       iommu_detach_device(domain, dev);
 }
-EXPORT_SYMBOL_GPL(rockchip_drm_dma_detach_device);
 
-int rockchip_register_crtc_funcs(struct drm_device *dev,
-                                const struct rockchip_crtc_funcs *crtc_funcs,
-                                int pipe)
+int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
+                                const struct rockchip_crtc_funcs *crtc_funcs)
 {
-       struct rockchip_drm_private *priv = dev->dev_private;
+       int pipe = drm_crtc_index(crtc);
+       struct rockchip_drm_private *priv = crtc->dev->dev_private;
 
        if (pipe > ROCKCHIP_MAX_CRTC)
                return -EINVAL;
@@ -77,18 +97,17 @@ int rockchip_register_crtc_funcs(struct drm_device *dev,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(rockchip_register_crtc_funcs);
 
-void rockchip_unregister_crtc_funcs(struct drm_device *dev, int pipe)
+void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
 {
-       struct rockchip_drm_private *priv = dev->dev_private;
+       int pipe = drm_crtc_index(crtc);
+       struct rockchip_drm_private *priv = crtc->dev->dev_private;
 
        if (pipe > ROCKCHIP_MAX_CRTC)
                return;
 
        priv->crtc_funcs[pipe] = NULL;
 }
-EXPORT_SYMBOL_GPL(rockchip_unregister_crtc_funcs);
 
 static struct drm_crtc *rockchip_crtc_from_pipe(struct drm_device *drm,
                                                int pipe)
@@ -130,17 +149,25 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev,
 static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
 {
        struct rockchip_drm_private *private;
-       struct dma_iommu_mapping *mapping;
        struct device *dev = drm_dev->dev;
        struct drm_connector *connector;
+       struct iommu_group *group;
        int ret;
 
        private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
        if (!private)
                return -ENOMEM;
 
+       mutex_init(&private->commit.lock);
+       INIT_WORK(&private->commit.work, rockchip_drm_atomic_work);
+
        drm_dev->dev_private = private;
 
+#ifdef CONFIG_DRM_DMA_SYNC
+       private->cpu_fence_context = fence_context_alloc(1);
+       atomic_set(&private->cpu_fence_seqno, 0);
+#endif
+
        drm_mode_config_init(drm_dev);
 
        rockchip_drm_mode_config_init(drm_dev);
@@ -152,23 +179,36 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
                goto err_config_cleanup;
        }
 
-       /* TODO(djkurtz): fetch the mapping start/size from somewhere */
-       mapping = arm_iommu_create_mapping(&platform_bus_type, 0x00000000,
-                                          SZ_2G);
-       if (IS_ERR(mapping)) {
-               ret = PTR_ERR(mapping);
-               goto err_config_cleanup;
-       }
+       private->domain = iommu_domain_alloc(&platform_bus_type);
+       if (!private->domain)
+               return -ENOMEM;
 
-       ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+       ret = iommu_get_dma_cookie(private->domain);
        if (ret)
-               goto err_release_mapping;
-
-       dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+               goto err_free_domain;
+
+       group = iommu_group_get(dev);
+       if (!group) {
+               group = iommu_group_alloc();
+               if (IS_ERR(group)) {
+                       dev_err(dev, "Failed to allocate IOMMU group\n");
+                       goto err_put_cookie;
+               }
 
-       ret = arm_iommu_attach_device(dev, mapping);
+               ret = iommu_group_add_device(group, dev);
+               iommu_group_put(group);
+               if (ret) {
+                       dev_err(dev, "failed to add device to IOMMU group\n");
+                       goto err_put_cookie;
+               }
+       }
+       /*
+        * Attach virtual iommu device, sub iommu device can share the same
+        * mapping with it.
+        */
+       ret = rockchip_drm_dma_attach_device(drm_dev, dev);
        if (ret)
-               goto err_release_mapping;
+               goto err_group_remove_device;
 
        /* Try to bind all sub drivers. */
        ret = component_bind_all(dev, drm_dev);
@@ -212,6 +252,8 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
         */
        drm_dev->vblank_disable_allowed = true;
 
+       drm_mode_config_reset(drm_dev);
+
        ret = rockchip_drm_fbdev_init(drm_dev);
        if (ret)
                goto err_vblank_cleanup;
@@ -224,9 +266,13 @@ err_kms_helper_poll_fini:
 err_unbind:
        component_unbind_all(dev, drm_dev);
 err_detach_device:
-       arm_iommu_detach_device(dev);
-err_release_mapping:
-       arm_iommu_release_mapping(dev->archdata.mapping);
+       rockchip_drm_dma_detach_device(drm_dev, dev);
+err_group_remove_device:
+       iommu_group_remove_device(dev);
+err_put_cookie:
+       iommu_put_dma_cookie(private->domain);
+err_free_domain:
+       iommu_domain_free(private->domain);
 err_config_cleanup:
        drm_mode_config_cleanup(drm_dev);
        drm_dev->dev_private = NULL;
@@ -236,19 +282,131 @@ err_config_cleanup:
 static int rockchip_drm_unload(struct drm_device *drm_dev)
 {
        struct device *dev = drm_dev->dev;
+       struct rockchip_drm_private *private = drm_dev->dev_private;
 
        rockchip_drm_fbdev_fini(drm_dev);
        drm_vblank_cleanup(drm_dev);
        drm_kms_helper_poll_fini(drm_dev);
        component_unbind_all(dev, drm_dev);
-       arm_iommu_detach_device(dev);
-       arm_iommu_release_mapping(dev->archdata.mapping);
+       rockchip_drm_dma_detach_device(drm_dev, dev);
+       iommu_group_remove_device(dev);
+       iommu_put_dma_cookie(private->domain);
+       iommu_domain_free(private->domain);
        drm_mode_config_cleanup(drm_dev);
        drm_dev->dev_private = NULL;
 
        return 0;
 }
 
+static void rockchip_drm_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
+                                                   struct drm_file *file_priv)
+{
+       struct rockchip_drm_private *priv = crtc->dev->dev_private;
+       int pipe = drm_crtc_index(crtc);
+
+       if (pipe < ROCKCHIP_MAX_CRTC &&
+           priv->crtc_funcs[pipe] &&
+           priv->crtc_funcs[pipe]->cancel_pending_vblank)
+               priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv);
+}
+
+int rockchip_drm_register_subdrv(struct drm_rockchip_subdrv *subdrv)
+{
+       if (!subdrv)
+               return -EINVAL;
+
+       mutex_lock(&subdrv_list_mutex);
+       list_add_tail(&subdrv->list, &rockchip_drm_subdrv_list);
+       mutex_unlock(&subdrv_list_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_register_subdrv);
+
+int rockchip_drm_unregister_subdrv(struct drm_rockchip_subdrv *subdrv)
+{
+       if (!subdrv)
+               return -EINVAL;
+
+       mutex_lock(&subdrv_list_mutex);
+       list_del(&subdrv->list);
+       mutex_unlock(&subdrv_list_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_unregister_subdrv);
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+       struct rockchip_drm_file_private *file_priv;
+       struct drm_rockchip_subdrv *subdrv;
+       int ret = 0;
+
+       file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
+       if (!file_priv)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&file_priv->gem_cpu_acquire_list);
+
+       file->driver_priv = file_priv;
+
+       mutex_lock(&subdrv_list_mutex);
+       list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
+               ret = subdrv->open(dev, subdrv->dev, file);
+               if (ret) {
+                       mutex_unlock(&subdrv_list_mutex);
+                       goto err_free_file_priv;
+               }
+       }
+       mutex_unlock(&subdrv_list_mutex);
+
+       return 0;
+
+err_free_file_priv:
+       kfree(file_priv);
+       file_priv = NULL;
+
+       return ret;
+}
+
+static void rockchip_drm_preclose(struct drm_device *dev,
+                                 struct drm_file *file_priv)
+{
+       struct rockchip_drm_file_private *file_private = file_priv->driver_priv;
+       struct rockchip_gem_object_node *cur, *d;
+       struct drm_rockchip_subdrv *subdrv;
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv);
+
+       mutex_lock(&dev->struct_mutex);
+       list_for_each_entry_safe(cur, d,
+                       &file_private->gem_cpu_acquire_list, list) {
+#ifdef CONFIG_DRM_DMA_SYNC
+               BUG_ON(!cur->rockchip_gem_obj->acquire_fence);
+               drm_fence_signal_and_put(&cur->rockchip_gem_obj->acquire_fence);
+#endif
+               drm_gem_object_unreference(&cur->rockchip_gem_obj->base);
+               kfree(cur);
+       }
+       /* since we are deleting the whole list, just initialize the header
+        * instead of calling list_del for every element
+        */
+       INIT_LIST_HEAD(&file_private->gem_cpu_acquire_list);
+       mutex_unlock(&dev->struct_mutex);
+
+       mutex_lock(&subdrv_list_mutex);
+       list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list)
+               subdrv->close(dev, subdrv->dev, file_priv);
+       mutex_unlock(&subdrv_list_mutex);
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev, struct drm_file *file)
+{
+       kfree(file->driver_priv);
+       file->driver_priv = NULL;
+}
+
 void rockchip_drm_lastclose(struct drm_device *dev)
 {
        struct rockchip_drm_private *priv = dev->dev_private;
@@ -256,6 +414,27 @@ void rockchip_drm_lastclose(struct drm_device *dev)
        drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper);
 }
 
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
+                         DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+                         rockchip_gem_map_offset_ioctl,
+                         DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_ACQUIRE,
+                         rockchip_gem_cpu_acquire_ioctl,
+                         DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_RELEASE,
+                         rockchip_gem_cpu_release_ioctl,
+                         DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_GET_VER, rockchip_rga_get_ver_ioctl,
+                         DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_SET_CMDLIST,
+                         rockchip_rga_set_cmdlist_ioctl,
+                         DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_EXEC, rockchip_rga_exec_ioctl,
+                         DRM_AUTH | DRM_RENDER_ALLOW),
+};
+
 static const struct file_operations rockchip_drm_driver_fops = {
        .owner = THIS_MODULE,
        .open = drm_open,
@@ -275,11 +454,16 @@ const struct vm_operations_struct rockchip_drm_vm_ops = {
 };
 
 static struct drm_driver rockchip_drm_driver = {
-       .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+       .driver_features        = DRIVER_MODESET | DRIVER_GEM |
+                                 DRIVER_PRIME | DRIVER_ATOMIC |
+                                 DRIVER_RENDER,
        .load                   = rockchip_drm_load,
        .unload                 = rockchip_drm_unload,
+       .preclose               = rockchip_drm_preclose,
        .lastclose              = rockchip_drm_lastclose,
        .get_vblank_counter     = drm_vblank_no_hw_counter,
+       .open                   = rockchip_drm_open,
+       .postclose              = rockchip_drm_postclose,
        .enable_vblank          = rockchip_drm_crtc_enable_vblank,
        .disable_vblank         = rockchip_drm_crtc_disable_vblank,
        .gem_vm_ops             = &rockchip_drm_vm_ops,
@@ -290,11 +474,14 @@ static struct drm_driver rockchip_drm_driver = {
        .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
        .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
        .gem_prime_import       = drm_gem_prime_import,
+       .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table,
        .gem_prime_export       = drm_gem_prime_export,
        .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
        .gem_prime_vmap         = rockchip_gem_prime_vmap,
        .gem_prime_vunmap       = rockchip_gem_prime_vunmap,
        .gem_prime_mmap         = rockchip_gem_mmap_buf,
+       .ioctls                 = rockchip_ioctls,
+       .num_ioctls             = ARRAY_SIZE(rockchip_ioctls),
        .fops                   = &rockchip_drm_driver_fops,
        .name   = DRIVER_NAME,
        .desc   = DRIVER_DESC,
@@ -381,36 +568,6 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = {
                                rockchip_drm_sys_resume)
 };
 
-/*
- * @node: device tree node containing encoder input ports
- * @encoder: drm_encoder
- */
-int rockchip_drm_encoder_get_mux_id(struct device_node *node,
-                                   struct drm_encoder *encoder)
-{
-       struct device_node *ep;
-       struct drm_crtc *crtc = encoder->crtc;
-       struct of_endpoint endpoint;
-       struct device_node *port;
-       int ret;
-
-       if (!node || !crtc)
-               return -EINVAL;
-
-       for_each_endpoint_of_node(node, ep) {
-               port = of_graph_get_remote_port(ep);
-               of_node_put(port);
-               if (port == crtc->port) {
-                       ret = of_graph_parse_endpoint(ep, &endpoint);
-                       of_node_put(ep);
-                       return ret ?: endpoint.id;
-               }
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(rockchip_drm_encoder_get_mux_id);
-
 static int compare_of(struct device *dev, void *data)
 {
        struct device_node *np = data;