Merge tag 'f2fs-for-v5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeu...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / drm_drv.c
index 05bbc2b622fc1094a2a8f85ce060d0805eae0f7e..862621494a93eb38c6f9a85b1914969d1bd1d4eb 100644 (file)
@@ -286,6 +286,138 @@ void drm_minor_release(struct drm_minor *minor)
  * Note that the lifetime rules for &drm_device instance has still a lot of
  * historical baggage. Hence use the reference counting provided by
  * drm_dev_get() and drm_dev_put() only carefully.
+ *
+ * Display driver example
+ * ~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The following example shows a typical structure of a DRM display driver.
+ * The example focus on the probe() function and the other functions that is
+ * almost always present and serves as a demonstration of devm_drm_dev_init()
+ * usage with its accompanying drm_driver->release callback.
+ *
+ * .. code-block:: c
+ *
+ *     struct driver_device {
+ *             struct drm_device drm;
+ *             void *userspace_facing;
+ *             struct clk *pclk;
+ *     };
+ *
+ *     static void driver_drm_release(struct drm_device *drm)
+ *     {
+ *             struct driver_device *priv = container_of(...);
+ *
+ *             drm_mode_config_cleanup(drm);
+ *             drm_dev_fini(drm);
+ *             kfree(priv->userspace_facing);
+ *             kfree(priv);
+ *     }
+ *
+ *     static struct drm_driver driver_drm_driver = {
+ *             [...]
+ *             .release = driver_drm_release,
+ *     };
+ *
+ *     static int driver_probe(struct platform_device *pdev)
+ *     {
+ *             struct driver_device *priv;
+ *             struct drm_device *drm;
+ *             int ret;
+ *
+ *             [
+ *               devm_kzalloc() can't be used here because the drm_device
+ *               lifetime can exceed the device lifetime if driver unbind
+ *               happens when userspace still has open file descriptors.
+ *             ]
+ *             priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ *             if (!priv)
+ *                     return -ENOMEM;
+ *
+ *             drm = &priv->drm;
+ *
+ *             ret = devm_drm_dev_init(&pdev->dev, drm, &driver_drm_driver);
+ *             if (ret) {
+ *                     kfree(drm);
+ *                     return ret;
+ *             }
+ *
+ *             drm_mode_config_init(drm);
+ *
+ *             priv->userspace_facing = kzalloc(..., GFP_KERNEL);
+ *             if (!priv->userspace_facing)
+ *                     return -ENOMEM;
+ *
+ *             priv->pclk = devm_clk_get(dev, "PCLK");
+ *             if (IS_ERR(priv->pclk))
+ *                     return PTR_ERR(priv->pclk);
+ *
+ *             [ Further setup, display pipeline etc ]
+ *
+ *             platform_set_drvdata(pdev, drm);
+ *
+ *             drm_mode_config_reset(drm);
+ *
+ *             ret = drm_dev_register(drm);
+ *             if (ret)
+ *                     return ret;
+ *
+ *             drm_fbdev_generic_setup(drm, 32);
+ *
+ *             return 0;
+ *     }
+ *
+ *     [ This function is called before the devm_ resources are released ]
+ *     static int driver_remove(struct platform_device *pdev)
+ *     {
+ *             struct drm_device *drm = platform_get_drvdata(pdev);
+ *
+ *             drm_dev_unregister(drm);
+ *             drm_atomic_helper_shutdown(drm)
+ *
+ *             return 0;
+ *     }
+ *
+ *     [ This function is called on kernel restart and shutdown ]
+ *     static void driver_shutdown(struct platform_device *pdev)
+ *     {
+ *             drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
+ *     }
+ *
+ *     static int __maybe_unused driver_pm_suspend(struct device *dev)
+ *     {
+ *             return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
+ *     }
+ *
+ *     static int __maybe_unused driver_pm_resume(struct device *dev)
+ *     {
+ *             drm_mode_config_helper_resume(dev_get_drvdata(dev));
+ *
+ *             return 0;
+ *     }
+ *
+ *     static const struct dev_pm_ops driver_pm_ops = {
+ *             SET_SYSTEM_SLEEP_PM_OPS(driver_pm_suspend, driver_pm_resume)
+ *     };
+ *
+ *     static struct platform_driver driver_driver = {
+ *             .driver = {
+ *                     [...]
+ *                     .pm = &driver_pm_ops,
+ *             },
+ *             .probe = driver_probe,
+ *             .remove = driver_remove,
+ *             .shutdown = driver_shutdown,
+ *     };
+ *     module_platform_driver(driver_driver);
+ *
+ * Drivers that want to support device unplugging (USB, DT overlay unload) should
+ * use drm_dev_unplug() instead of drm_dev_unregister(). The driver must protect
+ * regions that is accessing device resources to prevent use after they're
+ * released. This is done using drm_dev_enter() and drm_dev_exit(). There is one
+ * shortcoming however, drm_dev_unplug() marks the drm_device as unplugged before
+ * drm_atomic_helper_shutdown() is called. This means that if the disable code
+ * paths are protected, they will not run on regular driver module unload,
+ * possibily leaving the hardware enabled.
  */
 
 /**
@@ -376,7 +508,6 @@ void drm_dev_unplug(struct drm_device *dev)
        synchronize_srcu(&drm_unplug_srcu);
 
        drm_dev_unregister(dev);
-       drm_dev_put(dev);
 }
 EXPORT_SYMBOL(drm_dev_unplug);
 
@@ -452,6 +583,31 @@ static void drm_fs_inode_free(struct inode *inode)
        }
 }
 
+/**
+ * DOC: component helper usage recommendations
+ *
+ * DRM drivers that drive hardware where a logical device consists of a pile of
+ * independent hardware blocks are recommended to use the :ref:`component helper
+ * library<component>`. For consistency and better options for code reuse the
+ * following guidelines apply:
+ *
+ *  - The entire device initialization procedure should be run from the
+ *    &component_master_ops.master_bind callback, starting with drm_dev_init(),
+ *    then binding all components with component_bind_all() and finishing with
+ *    drm_dev_register().
+ *
+ *  - The opaque pointer passed to all components through component_bind_all()
+ *    should point at &struct drm_device of the device instance, not some driver
+ *    specific private structure.
+ *
+ *  - The component helper fills the niche where further standardization of
+ *    interfaces is not practical. When there already is, or will be, a
+ *    standardized interface like &drm_bridge or &drm_panel, providing its own
+ *    functions to find such components at driver load time, like
+ *    drm_of_find_panel_or_bridge(), then the component helper should not be
+ *    used.
+ */
+
 /**
  * drm_dev_init - Initialise new DRM device
  * @dev: DRM device
@@ -497,26 +653,22 @@ int drm_dev_init(struct drm_device *dev,
        BUG_ON(!parent);
 
        kref_init(&dev->ref);
-       dev->dev = parent;
+       dev->dev = get_device(parent);
        dev->driver = driver;
 
        /* no per-device feature limits by default */
        dev->driver_features = ~0u;
 
+       drm_legacy_init_members(dev);
        INIT_LIST_HEAD(&dev->filelist);
        INIT_LIST_HEAD(&dev->filelist_internal);
        INIT_LIST_HEAD(&dev->clientlist);
-       INIT_LIST_HEAD(&dev->ctxlist);
-       INIT_LIST_HEAD(&dev->vmalist);
-       INIT_LIST_HEAD(&dev->maplist);
        INIT_LIST_HEAD(&dev->vblank_event_list);
 
-       spin_lock_init(&dev->buf_lock);
        spin_lock_init(&dev->event_lock);
        mutex_init(&dev->struct_mutex);
        mutex_init(&dev->filelist_mutex);
        mutex_init(&dev->clientlist_mutex);
-       mutex_init(&dev->ctxlist_mutex);
        mutex_init(&dev->master_mutex);
 
        dev->anon_inode = drm_fs_inode_new();
@@ -536,7 +688,7 @@ int drm_dev_init(struct drm_device *dev,
        if (ret)
                goto err_minors;
 
-       ret = drm_ht_create(&dev->map_hash, 12);
+       ret = drm_legacy_create_map_hash(dev);
        if (ret)
                goto err_minors;
 
@@ -561,21 +713,61 @@ err_setunique:
                drm_gem_destroy(dev);
 err_ctxbitmap:
        drm_legacy_ctxbitmap_cleanup(dev);
-       drm_ht_remove(&dev->map_hash);
+       drm_legacy_remove_map_hash(dev);
 err_minors:
        drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);
        drm_fs_inode_free(dev->anon_inode);
 err_free:
+       put_device(dev->dev);
        mutex_destroy(&dev->master_mutex);
-       mutex_destroy(&dev->ctxlist_mutex);
        mutex_destroy(&dev->clientlist_mutex);
        mutex_destroy(&dev->filelist_mutex);
        mutex_destroy(&dev->struct_mutex);
+       drm_legacy_destroy_members(dev);
        return ret;
 }
 EXPORT_SYMBOL(drm_dev_init);
 
+static void devm_drm_dev_init_release(void *data)
+{
+       drm_dev_put(data);
+}
+
+/**
+ * devm_drm_dev_init - Resource managed drm_dev_init()
+ * @parent: Parent device object
+ * @dev: DRM device
+ * @driver: DRM driver
+ *
+ * Managed drm_dev_init(). The DRM device initialized with this function is
+ * automatically put on driver detach using drm_dev_put(). You must supply a
+ * &drm_driver.release callback to control the finalization explicitly.
+ *
+ * RETURNS:
+ * 0 on success, or error code on failure.
+ */
+int devm_drm_dev_init(struct device *parent,
+                     struct drm_device *dev,
+                     struct drm_driver *driver)
+{
+       int ret;
+
+       if (WARN_ON(!parent || !driver->release))
+               return -EINVAL;
+
+       ret = drm_dev_init(dev, driver, parent);
+       if (ret)
+               return ret;
+
+       ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
+       if (ret)
+               devm_drm_dev_init_release(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(devm_drm_dev_init);
+
 /**
  * drm_dev_fini - Finalize a dead DRM device
  * @dev: DRM device
@@ -596,17 +788,19 @@ void drm_dev_fini(struct drm_device *dev)
                drm_gem_destroy(dev);
 
        drm_legacy_ctxbitmap_cleanup(dev);
-       drm_ht_remove(&dev->map_hash);
+       drm_legacy_remove_map_hash(dev);
        drm_fs_inode_free(dev->anon_inode);
 
        drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);
 
+       put_device(dev->dev);
+
        mutex_destroy(&dev->master_mutex);
-       mutex_destroy(&dev->ctxlist_mutex);
        mutex_destroy(&dev->clientlist_mutex);
        mutex_destroy(&dev->filelist_mutex);
        mutex_destroy(&dev->struct_mutex);
+       drm_legacy_destroy_members(dev);
        kfree(dev->unique);
 }
 EXPORT_SYMBOL(drm_dev_fini);
@@ -840,8 +1034,6 @@ EXPORT_SYMBOL(drm_dev_register);
  */
 void drm_dev_unregister(struct drm_device *dev)
 {
-       struct drm_map_list *r_list, *list_temp;
-
        if (drm_core_check_feature(dev, DRIVER_LEGACY))
                drm_lastclose(dev);
 
@@ -858,8 +1050,7 @@ void drm_dev_unregister(struct drm_device *dev)
        if (dev->agp)
                drm_pci_agp_destroy(dev);
 
-       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
-               drm_legacy_rmmap(dev, r_list->map);
+       drm_legacy_rmmaps(dev);
 
        remove_compat_control_link(dev);
        drm_minor_unregister(dev, DRM_MINOR_PRIMARY);