blob: 554abd5d3b5382d565ad00b7f1aca1668e156b9e [file] [log] [blame]
/*
* Copyright (C) 2016 Noralf Trønnes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_print.h>
#include <drm/tinydrm/tinydrm.h>
#include <linux/device.h>
#include <linux/dma-buf.h>
#include <linux/module.h>
/**
* DOC: overview
*
* This library provides driver helpers for very simple display hardware.
*
* It is based on &drm_simple_display_pipe coupled with a &drm_connector which
* has only one fixed &drm_display_mode. The framebuffers are backed by the
* cma helper and have support for framebuffer flushing (dirty).
* fbdev support is also included.
*
*/
/**
* DOC: core
*
* The driver allocates &tinydrm_device, initializes it using
* devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
* and registers the DRM device using devm_tinydrm_register().
*/
static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
struct drm_driver *driver)
{
struct drm_device *drm;
/*
* We don't embed drm_device, because that prevent us from using
* devm_kzalloc() to allocate tinydrm_device in the driver since
* drm_dev_put() frees the structure. The devm_ functions provide
* for easy error handling.
*/
drm = drm_dev_alloc(driver, parent);
if (IS_ERR(drm))
return PTR_ERR(drm);
tdev->drm = drm;
drm->dev_private = tdev;
drm_mode_config_init(drm);
drm->mode_config.funcs = &tinydrm_mode_config_funcs;
drm->mode_config.allow_fb_modifiers = true;
return 0;
}
static void tinydrm_fini(struct tinydrm_device *tdev)
{
drm_mode_config_cleanup(tdev->drm);
tdev->drm->dev_private = NULL;
drm_dev_put(tdev->drm);
}
static void devm_tinydrm_release(void *data)
{
tinydrm_fini(data);
}
/**
* devm_tinydrm_init - Initialize tinydrm device
* @parent: Parent device object
* @tdev: tinydrm device
* @driver: DRM driver
*
* This function initializes @tdev, the underlying DRM device and it's
* mode_config. Resources will be automatically freed on driver detach (devres)
* using drm_mode_config_cleanup() and drm_dev_put().
*
* Returns:
* Zero on success, negative error code on failure.
*/
int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
struct drm_driver *driver)
{
int ret;
ret = tinydrm_init(parent, tdev, driver);
if (ret)
return ret;
ret = devm_add_action(parent, devm_tinydrm_release, tdev);
if (ret)
tinydrm_fini(tdev);
return ret;
}
EXPORT_SYMBOL(devm_tinydrm_init);
static int tinydrm_register(struct tinydrm_device *tdev)
{
struct drm_device *drm = tdev->drm;
int ret;
ret = drm_dev_register(tdev->drm, 0);
if (ret)
return ret;
ret = drm_fbdev_generic_setup(drm, 0);
if (ret)
DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
return 0;
}
static void tinydrm_unregister(struct tinydrm_device *tdev)
{
drm_atomic_helper_shutdown(tdev->drm);
drm_dev_unregister(tdev->drm);
}
static void devm_tinydrm_register_release(void *data)
{
tinydrm_unregister(data);
}
/**
* devm_tinydrm_register - Register tinydrm device
* @tdev: tinydrm device
*
* This function registers the underlying DRM device and fbdev.
* These resources will be automatically unregistered on driver detach (devres)
* and the display pipeline will be disabled.
*
* Returns:
* Zero on success, negative error code on failure.
*/
int devm_tinydrm_register(struct tinydrm_device *tdev)
{
struct device *dev = tdev->drm->dev;
int ret;
ret = tinydrm_register(tdev);
if (ret)
return ret;
ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
if (ret)
tinydrm_unregister(tdev);
return ret;
}
EXPORT_SYMBOL(devm_tinydrm_register);
/**
* tinydrm_shutdown - Shutdown tinydrm
* @tdev: tinydrm device
*
* This function makes sure that the display pipeline is disabled.
* Used by drivers in their shutdown callback to turn off the display
* on machine shutdown and reboot.
*/
void tinydrm_shutdown(struct tinydrm_device *tdev)
{
drm_atomic_helper_shutdown(tdev->drm);
}
EXPORT_SYMBOL(tinydrm_shutdown);
MODULE_LICENSE("GPL");