forked from Minki/linux
drm/drv: DOC: Add driver example code
Add driver example that shows how devm_drm_dev_init() can be used. v2: Expand docs (Sam, Daniel) Signed-off-by: Noralf Trønnes <noralf@tronnes.org> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190225144232.20761-4-noralf@tronnes.org
This commit is contained in:
parent
9b1f1b6b78
commit
de99f0600a
@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user