From patchwork Fri Apr 8 17:05:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Noralf_Tr=C3=B8nnes?= X-Patchwork-Id: 8784921 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 31BF6C0553 for ; Fri, 8 Apr 2016 17:05:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CDDC62034A for ; Fri, 8 Apr 2016 17:05:46 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id A1F1720351 for ; Fri, 8 Apr 2016 17:05:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D5FB06EAB4; Fri, 8 Apr 2016 17:05:39 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from asav22.altibox.net (asav22.altibox.net [109.247.116.9]) by gabe.freedesktop.org (Postfix) with ESMTPS id 30E4B6EAAC for ; Fri, 8 Apr 2016 17:05:37 +0000 (UTC) Received: from localhost.localdomain (151.79-160-56.customer.lyse.net [79.160.56.151]) by asav22.altibox.net (Postfix) with ESMTP id BDD862018D; Fri, 8 Apr 2016 19:05:34 +0200 (CEST) From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= To: dri-devel@lists.freedesktop.org Subject: [RFC v2 4/8] drm: Add DRM support for tiny LCD displays Date: Fri, 8 Apr 2016 19:05:06 +0200 Message-Id: <1460135110-24121-5-git-send-email-noralf@tronnes.org> X-Mailer: git-send-email 2.2.2 In-Reply-To: <1460135110-24121-1-git-send-email-noralf@tronnes.org> References: <1460135110-24121-1-git-send-email-noralf@tronnes.org> MIME-Version: 1.0 X-CMAE-Score: 0 X-CMAE-Analysis: v=2.1 cv=H7l7u7si c=1 sm=1 tr=0 a=gFHx44SYZz5JQKQKbGEAEQ==:117 a=gFHx44SYZz5JQKQKbGEAEQ==:17 a=L9H7d07YOLsA:10 a=9cW_t1CCXrUA:10 a=s5jvgZ67dGcA:10 a=IkcTkHD0fZMA:10 a=SJz97ENfAAAA:8 a=OWh5ijxnrYAWI1xINE8A:9 a=6XBDyIfiewPHuSFb:21 a=20SqVK8G3L8LXV_t:21 a=QEXdDO2ut3YA:10 Cc: thomas.petazzoni@free-electrons.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include +#include +#include + +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 +#include +#include +#include +#include + +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 */