[RFC v2 4/8] drm: Add DRM support for tiny LCD displays
Noralf Trønnes
noralf at tronnes.org
Fri Apr 8 17:05:06 UTC 2016
tinydrm provides a very simplified view of DRM for displays that has
onboard video memory and is connected through a slow bus like SPI/I2C.
Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/tinydrm/Kconfig | 11 ++
drivers/gpu/drm/tinydrm/Makefile | 1 +
drivers/gpu/drm/tinydrm/core/Makefile | 6 +
drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 155 +++++++++++++++++++++
.../gpu/drm/tinydrm/core/tinydrm-display-pipe.c | 82 +++++++++++
drivers/gpu/drm/tinydrm/core/tinydrm-fbdev.c | 94 +++++++++++++
drivers/gpu/drm/tinydrm/core/tinydrm-framebuffer.c | 99 +++++++++++++
drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c | 95 +++++++++++++
include/drm/tinydrm/tinydrm.h | 143 +++++++++++++++++++
11 files changed, 689 insertions(+)
create mode 100644 drivers/gpu/drm/tinydrm/Kconfig
create mode 100644 drivers/gpu/drm/tinydrm/Makefile
create mode 100644 drivers/gpu/drm/tinydrm/core/Makefile
create mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-core.c
create mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-display-pipe.c
create mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-fbdev.c
create mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-framebuffer.c
create mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
create mode 100644 include/drm/tinydrm/tinydrm.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index cb62cd9..b495dbf 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -276,3 +276,5 @@ source "drivers/gpu/drm/imx/Kconfig"
source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig"
+
+source "drivers/gpu/drm/tinydrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index ea9bf59..184056e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -75,3 +75,4 @@ obj-y += panel/
obj-y += bridge/
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
+obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
new file mode 100644
index 0000000..e26e5ed
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -0,0 +1,11 @@
+menuconfig DRM_TINYDRM
+ tristate "Support for small TFT LCD display modules"
+ depends on DRM
+ select DRM_SIMPLE_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_PANEL
+ select VIDEOMODE_HELPERS
+ select FB_DEFERRED_IO if DRM_KMS_FB_HELPER
+ help
+ Choose this option if you have a tinydrm supported display.
+ If M is selected the module will be called tinydrm.
diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile
new file mode 100644
index 0000000..7476ed1
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_TINYDRM) += core/
diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
new file mode 100644
index 0000000..11366b4
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
+tinydrm-y += tinydrm-core.o
+tinydrm-y += tinydrm-display-pipe.o
+tinydrm-y += tinydrm-framebuffer.o
+tinydrm-y += tinydrm-helpers.o
+tinydrm-$(CONFIG_DRM_KMS_FB_HELPER) += tinydrm-fbdev.o
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
new file mode 100644
index 0000000..131a2ac
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
@@ -0,0 +1,155 @@
+//#define DEBUG
+/*
+ * 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/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/tinydrm/tinydrm.h>
+#include <linux/device.h>
+
+static const uint32_t tinydrm_formats[] = {
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB8888,
+};
+
+static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
+ .fb_create = tinydrm_fb_cma_dumb_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+void tinydrm_lastclose(struct drm_device *dev)
+{
+ struct tinydrm_device *tdev = dev->dev_private;
+
+ DRM_DEBUG_KMS("\n");
+ tinydrm_fbdev_restore_mode(tdev);
+}
+EXPORT_SYMBOL(tinydrm_lastclose);
+
+const struct file_operations tinydrm_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+EXPORT_SYMBOL(tinydrm_fops);
+
+static void tinydrm_unregister(struct tinydrm_device *tdev)
+{
+ DRM_DEBUG_KMS("\n");
+
+ tinydrm_fbdev_fini(tdev);
+
+ drm_mode_config_cleanup(tdev->base);
+ drm_dev_unregister(tdev->base);
+ drm_dev_unref(tdev->base);
+}
+
+static int tinydrm_register(struct device *parent, struct tinydrm_device *tdev,
+ struct drm_driver *driver)
+{
+ struct drm_device *dev;
+ int ret;
+
+ DRM_DEBUG_KMS("\n");
+
+ if (WARN_ON(!tdev->dirtyfb))
+ return -EINVAL;
+
+ if (!parent->coherent_dma_mask) {
+ ret = dma_set_coherent_mask(parent, DMA_BIT_MASK(32));
+ if (ret) {
+ DRM_ERROR("Failed to set coherent_dma_mask\n");
+ return ret;
+ }
+ }
+
+ dev = drm_dev_alloc(driver, parent);
+ if (!dev)
+ return -ENOMEM;
+
+ tdev->base = dev;
+ dev->dev_private = tdev;
+
+ ret = drm_dev_set_unique(dev, dev_name(dev->dev));
+ if (ret)
+ goto err_free;
+
+ ret = drm_dev_register(dev, 0);
+ if (ret)
+ goto err_free;
+
+ drm_mode_config_init(dev);
+ dev->mode_config.min_width = tdev->width;
+ dev->mode_config.min_height = tdev->height;
+ dev->mode_config.max_width = tdev->width;
+ dev->mode_config.max_height = tdev->height;
+ dev->mode_config.funcs = &tinydrm_mode_config_funcs;
+
+ ret = tinydrm_display_pipe_init(tdev, tinydrm_formats,
+ ARRAY_SIZE(tinydrm_formats));
+ if (ret)
+ goto err_free;
+
+ drm_mode_config_reset(dev);
+
+ ret = tinydrm_fbdev_init(tdev);
+ if (ret)
+ DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
+
+ DRM_INFO("Device: %s\n", dev_name(dev->dev));
+ DRM_INFO("Initialized %s %d.%d.%d on minor %d\n",
+ driver->name, driver->major, driver->minor, driver->patchlevel,
+ dev->primary->index);
+
+ return 0;
+
+err_free:
+ drm_dev_unref(dev);
+
+ return ret;
+}
+
+static void devm_tinydrm_release(struct device *dev, void *res)
+{
+ tinydrm_unregister(*(struct tinydrm_device **)res);
+}
+
+int devm_tinydrm_register(struct device *dev, struct tinydrm_device *tdev,
+ struct drm_driver *driver)
+{
+ struct tinydrm_device **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_tinydrm_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = tinydrm_register(dev, tdev, driver);
+ if (ret) {
+ devres_free(ptr);
+ return ret;
+ }
+
+ *ptr = tdev;
+ devres_add(dev, ptr);
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_tinydrm_register);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-display-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-display-pipe.c
new file mode 100644
index 0000000..5e5fa3c
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-display-pipe.c
@@ -0,0 +1,82 @@
+/*
+ * 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_crtc.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/tinydrm/tinydrm.h>
+
+static void tinydrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
+ struct drm_crtc_state *crtc_state)
+{
+ struct tinydrm_device *tdev;
+
+ tdev = container_of(pipe, struct tinydrm_device, pipe);
+ DRM_DEBUG_KMS("prepared=%u, enabled=%u\n", tdev->prepared, tdev->enabled);
+
+ /* The panel must be prepared on the first crtc enable after probe */
+ tinydrm_prepare(tdev);
+ /* The panel is enabled after the first display update */
+}
+
+static void tinydrm_display_pipe_disable(struct drm_simple_display_pipe *pipe)
+{
+ struct tinydrm_device *tdev;
+
+ tdev = container_of(pipe, struct tinydrm_device, pipe);
+ DRM_DEBUG_KMS("prepared=%u, enabled=%u\n", tdev->prepared, tdev->enabled);
+
+ tinydrm_disable(tdev);
+}
+
+struct drm_simple_display_pipe_funcs tinydrm_display_pipe_funcs = {
+ .enable = tinydrm_display_pipe_enable,
+ .disable = tinydrm_display_pipe_disable,
+};
+
+int tinydrm_display_pipe_init(struct tinydrm_device *tdev,
+ const uint32_t *formats, unsigned int format_count)
+{
+ struct drm_device *dev = tdev->base;
+ struct drm_connector *connector;
+ int ret;
+
+ connector = drm_simple_kms_panel_connector_create(dev, &tdev->panel,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ if (IS_ERR(connector))
+ return PTR_ERR(connector);
+
+ ret = drm_simple_display_pipe_init(dev, &tdev->pipe,
+ &tinydrm_display_pipe_funcs,
+ formats, format_count,
+ connector);
+
+ return ret;
+}
+EXPORT_SYMBOL(tinydrm_display_pipe_init);
+
+int tinydrm_panel_get_modes(struct drm_panel *panel)
+{
+ struct drm_display_mode *mode;
+ struct tinydrm_device *tdev;
+
+ tdev = container_of(panel, struct tinydrm_device, panel);
+// TODO: get width/height somewhere else
+ mode = drm_cvt_mode(panel->connector->dev, tdev->width, tdev->height,
+ 60, false, false, false);
+ if (!mode)
+ return 0;
+
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(panel->connector, mode);
+
+ return 1;
+}
+EXPORT_SYMBOL(tinydrm_panel_get_modes);
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-fbdev.c b/drivers/gpu/drm/tinydrm/core/tinydrm-fbdev.c
new file mode 100644
index 0000000..73013b4f
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-fbdev.c
@@ -0,0 +1,94 @@
+/*
+ * 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/drmP.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/tinydrm/tinydrm.h>
+
+static int tinydrm_fbdev_fb_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct drm_gem_cma_object *cma = drm_fb_cma_get_gem_obj(fb, 0);
+ struct tinydrm_device *tdev = fb->dev->dev_private;
+
+ if (tdev->pipe.plane.fb != fb)
+ return 0;
+
+ return tdev->dirtyfb(fb, cma->vaddr, flags, color, clips, num_clips);
+}
+
+static struct drm_framebuffer_funcs tinydrm_fbdev_fb_funcs = {
+ .destroy = drm_fb_cma_destroy,
+ .create_handle = drm_fb_cma_create_handle,
+ .dirty = tinydrm_fbdev_fb_dirty,
+};
+
+static int tinydrm_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct tinydrm_device *tdev = helper->dev->dev_private;
+ int ret;
+
+ ret = drm_fbdev_cma_create_with_funcs(helper, sizes,
+ &tinydrm_fbdev_fb_funcs);
+ if (ret)
+ return ret;
+
+ if (tdev->fbdefio_delay_ms) {
+ unsigned long delay;
+
+ delay = msecs_to_jiffies(tdev->fbdefio_delay_ms);
+ helper->fbdev->fbdefio->delay = delay ? delay : 1;
+ }
+
+ return 0;
+}
+
+static const struct drm_fb_helper_funcs tinydrm_fb_helper_funcs = {
+ .fb_probe = tinydrm_fbdev_create,
+};
+
+int tinydrm_fbdev_init(struct tinydrm_device *tdev)
+{
+ struct drm_device *dev = tdev->base;
+ struct drm_fbdev_cma *fbdev;
+
+ DRM_DEBUG_KMS("IN\n");
+
+ fbdev = drm_fbdev_cma_init_with_funcs(dev, 16,
+ dev->mode_config.num_crtc,
+ dev->mode_config.num_connector,
+ &tinydrm_fb_helper_funcs);
+ if (IS_ERR(fbdev))
+ return PTR_ERR(fbdev);
+
+ tdev->fbdev_cma = fbdev;
+
+ DRM_DEBUG_KMS("OUT\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(tinydrm_fbdev_init);
+
+void tinydrm_fbdev_fini(struct tinydrm_device *tdev)
+{
+ drm_fbdev_cma_fini(tdev->fbdev_cma);
+ tdev->fbdev_cma = NULL;
+}
+EXPORT_SYMBOL(tinydrm_fbdev_fini);
+
+void tinydrm_fbdev_restore_mode(struct tinydrm_device *tdev)
+{
+ drm_fbdev_cma_restore_mode(tdev->fbdev_cma);
+}
+EXPORT_SYMBOL(tinydrm_fbdev_restore_mode);
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-framebuffer.c b/drivers/gpu/drm/tinydrm/core/tinydrm-framebuffer.c
new file mode 100644
index 0000000..e167f92
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-framebuffer.c
@@ -0,0 +1,99 @@
+/*
+ * 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/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/tinydrm/tinydrm.h>
+
+struct tinydrm_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_gem_cma_object *cma_obj;
+};
+
+static inline struct tinydrm_framebuffer *to_tinydrm_framebuffer(struct drm_framebuffer *fb)
+{
+ return container_of(fb, struct tinydrm_framebuffer, base);
+}
+
+static void tinydrm_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct tinydrm_framebuffer *tinydrm_fb = to_tinydrm_framebuffer(fb);
+
+ DRM_DEBUG_KMS("fb = %p, cma_obj = %p\n", fb, tinydrm_fb->cma_obj);
+
+ if (tinydrm_fb->cma_obj)
+ drm_gem_object_unreference_unlocked(&tinydrm_fb->cma_obj->base);
+
+ drm_framebuffer_cleanup(fb);
+ kfree(tinydrm_fb);
+}
+
+static int tinydrm_framebuffer_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct tinydrm_framebuffer *tfb = to_tinydrm_framebuffer(fb);
+ struct tinydrm_device *tdev = fb->dev->dev_private;
+
+ dev_dbg(fb->dev->dev, "%s\n", __func__);
+
+ return tdev->dirtyfb(fb, tfb->cma_obj->vaddr, flags, color, clips, num_clips);
+}
+
+static const struct drm_framebuffer_funcs tinydrm_fb_funcs = {
+ .destroy = tinydrm_framebuffer_destroy,
+ .dirty = tinydrm_framebuffer_dirty,
+/* TODO?
+ * .create_handle = tinydrm_framebuffer_create_handle, */
+};
+
+/*
+ * Maybe this could be turned into drm_fb_cma_dumb_create_with_funcs() and put
+ * alongside drm_fb_cma_create() in drm_fb_cma_helper.c
+ */
+struct drm_framebuffer *tinydrm_fb_cma_dumb_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct tinydrm_framebuffer *tinydrm_fb;
+ struct drm_gem_object *obj;
+ int ret;
+
+ /* TODO? Validate the pixel format, size and pitches */
+ DRM_DEBUG_KMS("pixel_format=%s\n", drm_get_format_name(mode_cmd->pixel_format));
+ DRM_DEBUG_KMS("width=%u\n", mode_cmd->width);
+ DRM_DEBUG_KMS("height=%u\n", mode_cmd->height);
+ DRM_DEBUG_KMS("pitches[0]=%u\n", mode_cmd->pitches[0]);
+
+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+ if (!obj)
+ return NULL;
+
+ tinydrm_fb = kzalloc(sizeof(*tinydrm_fb), GFP_KERNEL);
+ if (!tinydrm_fb)
+ return NULL;
+
+ tinydrm_fb->cma_obj = to_drm_gem_cma_obj(obj);
+
+ ret = drm_framebuffer_init(dev, &tinydrm_fb->base, &tinydrm_fb_funcs);
+ if (ret) {
+ kfree(tinydrm_fb);
+ drm_gem_object_unreference_unlocked(obj);
+ return NULL;
+ }
+
+ drm_helper_mode_fill_fb_struct(&tinydrm_fb->base, mode_cmd);
+
+ return &tinydrm_fb->base;
+}
+EXPORT_SYMBOL(tinydrm_fb_cma_dumb_create);
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
new file mode 100644
index 0000000..3545d7f
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
@@ -0,0 +1,95 @@
+/*
+ * 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/drmP.h>
+#include <drm/tinydrm/tinydrm.h>
+#include <linux/backlight.h>
+#include <linux/spi/spi.h>
+
+struct backlight_device *tinydrm_of_find_backlight(struct device *dev)
+{
+ struct backlight_device *backlight;
+ struct device_node *np;
+
+ np = of_parse_phandle(dev->of_node, "backlight", 0);
+ if (!np)
+ return NULL;
+
+ backlight = of_find_backlight_by_node(np);
+ of_node_put(np);
+
+ if (!backlight)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return backlight;
+}
+EXPORT_SYMBOL(tinydrm_of_find_backlight);
+
+int tinydrm_panel_enable_backlight(struct drm_panel *panel)
+{
+ struct tinydrm_device *tdev = tinydrm_from_panel(panel);
+
+ if (tdev->backlight) {
+ if (tdev->backlight->props.brightness == 0)
+ tdev->backlight->props.brightness =
+ tdev->backlight->props.max_brightness;
+ tdev->backlight->props.state &= ~BL_CORE_SUSPENDED;
+ backlight_update_status(tdev->backlight);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tinydrm_panel_enable_backlight);
+
+int tinydrm_panel_disable_backlight(struct drm_panel *panel)
+{
+ struct tinydrm_device *tdev = tinydrm_from_panel(panel);
+
+ if (tdev->backlight) {
+ tdev->backlight->props.state |= BL_CORE_SUSPENDED;
+ backlight_update_status(tdev->backlight);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tinydrm_panel_disable_backlight);
+
+static int __maybe_unused tinydrm_pm_suspend(struct device *dev)
+{
+ struct tinydrm_device *tdev = dev_get_drvdata(dev);
+
+ tinydrm_disable(tdev);
+ tinydrm_unprepare(tdev);
+
+ return 0;
+}
+
+static int __maybe_unused tinydrm_pm_resume(struct device *dev)
+{
+ struct tinydrm_device *tdev = dev_get_drvdata(dev);
+
+ tinydrm_prepare(tdev);
+ /* The panel is enabled after the first display update */
+
+ return 0;
+}
+
+const struct dev_pm_ops tinydrm_simple_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tinydrm_pm_suspend, tinydrm_pm_resume)
+};
+EXPORT_SYMBOL(tinydrm_simple_pm_ops);
+
+void tinydrm_spi_shutdown(struct spi_device *spi)
+{
+ struct tinydrm_device *tdev = spi_get_drvdata(spi);
+
+ tinydrm_disable(tdev);
+ tinydrm_unprepare(tdev);
+}
+EXPORT_SYMBOL(tinydrm_spi_shutdown);
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
new file mode 100644
index 0000000..5cd5e62
--- /dev/null
+++ b/include/drm/tinydrm/tinydrm.h
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+#ifndef __LINUX_TINYDRM_H
+#define __LINUX_TINYDRM_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct spi_device;
+struct regulator;
+struct lcdreg;
+
+struct tinydrm_device {
+ struct drm_device *base;
+ u32 width, height;
+ struct drm_simple_display_pipe pipe;
+ struct drm_panel panel;
+ struct drm_fbdev_cma *fbdev_cma;
+ unsigned fbdefio_delay_ms;
+ struct backlight_device *backlight;
+ struct regulator *regulator;
+ struct lcdreg *lcdreg;
+ bool prepared;
+ bool enabled;
+ void *dev_private;
+
+ int (*dirtyfb)(struct drm_framebuffer *fb, void *vmem, unsigned flags,
+ unsigned color, struct drm_clip_rect *clips,
+ unsigned num_clips);
+};
+
+extern const struct file_operations tinydrm_fops;
+void tinydrm_lastclose(struct drm_device *dev);
+
+#define TINYDRM_DRM_DRIVER(name_struct, name_str, desc_str, date_str) \
+static struct drm_driver name_struct = { \
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME \
+ | DRIVER_ATOMIC, \
+ .lastclose = tinydrm_lastclose, \
+ .gem_free_object = drm_gem_cma_free_object, \
+ .gem_vm_ops = &drm_gem_cma_vm_ops, \
+ .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_export = drm_gem_prime_export, \
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, \
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, \
+ .gem_prime_vmap = drm_gem_cma_prime_vmap, \
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap, \
+ .gem_prime_mmap = drm_gem_cma_prime_mmap, \
+ .dumb_create = drm_gem_cma_dumb_create, \
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset, \
+ .dumb_destroy = drm_gem_dumb_destroy, \
+ .fops = &tinydrm_fops, \
+ .name = name_str, \
+ .desc = desc_str, \
+ .date = date_str, \
+ .major = 1, \
+ .minor = 0, \
+}
+
+struct drm_framebuffer *tinydrm_fb_cma_dumb_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd);
+int tinydrm_display_pipe_init(struct tinydrm_device *tdev,
+ const uint32_t *formats, unsigned int format_count);
+int tinydrm_panel_get_modes(struct drm_panel *panel);
+int devm_tinydrm_register(struct device *dev, struct tinydrm_device *tdev,
+ struct drm_driver *driver);
+
+static inline struct tinydrm_device *tinydrm_from_panel(struct drm_panel *panel)
+{
+ return panel->connector->dev->dev_private;
+}
+
+static inline void tinydrm_prepare(struct tinydrm_device *tdev)
+{
+ if (!tdev->prepared) {
+ drm_panel_prepare(&tdev->panel);
+ tdev->prepared = true;
+ }
+}
+
+static inline void tinydrm_unprepare(struct tinydrm_device *tdev)
+{
+ if (tdev->prepared) {
+ drm_panel_unprepare(&tdev->panel);
+ tdev->prepared = false;
+ }
+}
+
+static inline void tinydrm_enable(struct tinydrm_device *tdev)
+{
+ if (!tdev->enabled) {
+ drm_panel_enable(&tdev->panel);
+ tdev->enabled = true;
+ }
+}
+
+static inline void tinydrm_disable(struct tinydrm_device *tdev)
+{
+ if (tdev->enabled) {
+ drm_panel_disable(&tdev->panel);
+ tdev->enabled = false;
+ }
+}
+
+#ifdef CONFIG_DRM_KMS_FB_HELPER
+int tinydrm_fbdev_init(struct tinydrm_device *tdev);
+void tinydrm_fbdev_fini(struct tinydrm_device *tdev);
+void tinydrm_fbdev_restore_mode(struct tinydrm_device *tdev);
+#else
+static inline int tinydrm_fbdev_init(struct tinydrm_device *tdev)
+{
+ return 0;
+}
+
+static inline void tinydrm_fbdev_fini(struct tinydrm_device *tdev)
+{
+}
+
+static inline void tinydrm_fbdev_restore_mode(struct tinydrm_device *tdev)
+{
+}
+#endif
+
+struct backlight_device *tinydrm_of_find_backlight(struct device *dev);
+int tinydrm_panel_enable_backlight(struct drm_panel *panel);
+int tinydrm_panel_disable_backlight(struct drm_panel *panel);
+extern const struct dev_pm_ops tinydrm_simple_pm_ops;
+void tinydrm_spi_shutdown(struct spi_device *spi);
+
+#endif /* __LINUX_TINYDRM_H */
--
2.2.2
More information about the dri-devel
mailing list