From patchwork Sun Aug 14 16:52:05 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: 9279499 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1625960231 for ; Sun, 14 Aug 2016 16:52:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0742A28A28 for ; Sun, 14 Aug 2016 16:52:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EF5A128A57; Sun, 14 Aug 2016 16:52:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id C3BB428A28 for ; Sun, 14 Aug 2016 16:52:30 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 235096E075; Sun, 14 Aug 2016 16:52:27 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from smtp.domeneshop.no (smtp.domeneshop.no [IPv6:2a01:5b40:0:3005::1]) by gabe.freedesktop.org (Postfix) with ESMTPS id C57E36E074 for ; Sun, 14 Aug 2016 16:52:25 +0000 (UTC) Received: from 211.81-166-168.customer.lyse.net ([81.166.168.211]:53067 helo=localhost.localdomain) by smtp.domeneshop.no with esmtpsa (TLS1.1:DHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.80) (envelope-from ) id 1bYye8-0007ha-5O; Sun, 14 Aug 2016 18:52:24 +0200 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= To: dri-devel@lists.freedesktop.org Subject: [PATCH v3 2/3] drm: simpledrm: add fbdev fallback support Date: Sun, 14 Aug 2016 18:52:05 +0200 Message-Id: <1471193526-22844-3-git-send-email-noralf@tronnes.org> X-Mailer: git-send-email 2.8.2 In-Reply-To: <1471193526-22844-1-git-send-email-noralf@tronnes.org> References: <1471193526-22844-1-git-send-email-noralf@tronnes.org> MIME-Version: 1.0 Cc: linux-kernel@vger.kernel.org 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-Virus-Scanned: ClamAV using ClamSMTP Create a simple fbdev device during SimpleDRM setup so legacy user-space and fbcon can use it. Original work by David Herrmann. Cc: dh.herrmann@gmail.com Signed-off-by: Noralf Trønnes --- Changes from version 2: - Switch to using drm_fb_helper in preparation for future panic handling which needs an enabled pipeline. Changes from version 1: No changes Changes from previous version: - Remove the DRM_SIMPLEDRM_FBDEV kconfig option and use DRM_FBDEV_EMULATION - Suspend fbcon/fbdev when the pipeline is enabled, resume in lastclose - Add FBINFO_CAN_FORCE_OUTPUT flag so we get oops'es on the console drivers/gpu/drm/simpledrm/Kconfig | 3 + drivers/gpu/drm/simpledrm/Makefile | 1 + drivers/gpu/drm/simpledrm/simpledrm.h | 32 ++++- drivers/gpu/drm/simpledrm/simpledrm_drv.c | 4 + drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 201 ++++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_kms.c | 10 +- 6 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c -- 2.8.2 diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig index f45b25d..9454536 100644 --- a/drivers/gpu/drm/simpledrm/Kconfig +++ b/drivers/gpu/drm/simpledrm/Kconfig @@ -13,6 +13,9 @@ config DRM_SIMPLEDRM SimpleDRM supports "simple-framebuffer" DeviceTree objects and compatible platform framebuffers. + If fbdev support is enabled, this driver will also provide an fbdev + compatibility layer. + If unsure, say Y. To compile this driver as a module, choose M here: the diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile index f6a62dc..7087245 100644 --- a/drivers/gpu/drm/simpledrm/Makefile +++ b/drivers/gpu/drm/simpledrm/Makefile @@ -1,4 +1,5 @@ simpledrm-y := simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \ simpledrm_damage.o +simpledrm-$(CONFIG_DRM_FBDEV_EMULATION) += simpledrm_fbdev.o obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h index 481acc2..f01b75d 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm.h +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -17,13 +17,13 @@ struct simplefb_format; struct regulator; -struct fb_info; struct clk; struct sdrm_device { struct drm_device *ddev; struct drm_simple_display_pipe pipe; struct drm_connector conn; + struct sdrm_fbdev *fbdev; /* framebuffer information */ const struct simplefb_format *fb_sformat; @@ -46,6 +46,7 @@ struct sdrm_device { #endif }; +void sdrm_lastclose(struct drm_device *ddev); int sdrm_drm_modeset_init(struct sdrm_device *sdrm); int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma); @@ -87,4 +88,33 @@ struct sdrm_framebuffer { #define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base) +#ifdef CONFIG_DRM_FBDEV_EMULATION + +void sdrm_fbdev_init(struct sdrm_device *sdrm); +void sdrm_fbdev_cleanup(struct sdrm_device *sdrm); +void sdrm_fbdev_display_pipe_update(struct sdrm_device *sdrm, + struct drm_framebuffer *fb); +void sdrm_fbdev_restore_mode(struct sdrm_device *sdrm); + +#else + +static inline void sdrm_fbdev_init(struct sdrm_device *sdrm) +{ +} + +static inline void sdrm_fbdev_cleanup(struct sdrm_device *sdrm) +{ +} + +static inline void sdrm_fbdev_display_pipe_update(struct sdrm_device *sdrm, + struct drm_framebuffer *fb) +{ +} + +static inline void sdrm_fbdev_restore_mode(struct sdrm_device *sdrm) +{ +} + +#endif + #endif /* SDRM_DRV_H */ diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c index e5b6f75..a4e6566 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -42,6 +42,7 @@ static struct drm_driver sdrm_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &sdrm_drm_fops, + .lastclose = sdrm_lastclose, .gem_free_object = sdrm_gem_free_object, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, @@ -448,6 +449,8 @@ static int sdrm_simplefb_probe(struct platform_device *pdev) if (ret) goto err_regulators; + sdrm_fbdev_init(ddev->dev_private); + DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name, ddev->primary->index); @@ -473,6 +476,7 @@ static int sdrm_simplefb_remove(struct platform_device *pdev) struct drm_device *ddev = platform_get_drvdata(pdev); struct sdrm_device *sdrm = ddev->dev_private; + sdrm_fbdev_cleanup(sdrm); drm_dev_unregister(ddev); drm_mode_config_cleanup(ddev); diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c new file mode 100644 index 0000000..4038dd9 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c @@ -0,0 +1,201 @@ +/* + * SimpleDRM firmware framebuffer driver + * Copyright (c) 2012-2014 David Herrmann + * + * 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 +#include +#include + +#include "simpledrm.h" + +struct sdrm_fbdev { + struct drm_fb_helper fb_helper; + struct drm_framebuffer fb; +}; + +static inline struct sdrm_fbdev *to_sdrm_fbdev(struct drm_fb_helper *helper) +{ + return container_of(helper, struct sdrm_fbdev, fb_helper); +} + +static struct fb_ops sdrm_fbdev_ops = { + .owner = THIS_MODULE, + .fb_fillrect = drm_fb_helper_cfb_fillrect, + .fb_copyarea = drm_fb_helper_cfb_copyarea, + .fb_imageblit = drm_fb_helper_cfb_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static struct drm_framebuffer_funcs sdrm_fb_funcs = { + .destroy = drm_framebuffer_cleanup, +}; + +static int sdrm_fbdev_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct sdrm_fbdev *fbdev = to_sdrm_fbdev(helper); + struct drm_device *ddev = helper->dev; + struct sdrm_device *sdrm = ddev->dev_private; + struct drm_framebuffer *fb = &fbdev->fb; + struct drm_mode_fb_cmd2 mode_cmd = { + .width = sdrm->fb_width, + .height = sdrm->fb_height, + .pitches[0] = sdrm->fb_stride, + .pixel_format = sdrm->fb_format, + }; + struct fb_info *fbi; + int ret; + + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) + return PTR_ERR(fbi); + + drm_helper_mode_fill_fb_struct(fb, &mode_cmd); + + ret = drm_framebuffer_init(ddev, fb, &sdrm_fb_funcs); + if (ret) { + dev_err(ddev->dev, "Failed to initialize framebuffer: %d\n", ret); + goto err_fb_info_destroy; + } + + helper->fb = fb; + fbi->par = helper; + + fbi->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE | + FBINFO_CAN_FORCE_OUTPUT; + fbi->fbops = &sdrm_fbdev_ops; + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); + + strncpy(fbi->fix.id, "simpledrmfb", 15); + fbi->fix.smem_start = (unsigned long)sdrm->fb_base; + fbi->fix.smem_len = sdrm->fb_size; + fbi->screen_base = sdrm->fb_map; + + return 0; + +err_fb_info_destroy: + drm_fb_helper_release_fbi(helper); + + return ret; +} + +static const struct drm_fb_helper_funcs sdrm_fb_helper_funcs = { + .fb_probe = sdrm_fbdev_create, +}; + +void sdrm_fbdev_init(struct sdrm_device *sdrm) +{ + struct drm_device *ddev = sdrm->ddev; + struct drm_fb_helper *fb_helper; + struct sdrm_fbdev *fbdev; + int ret; + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) { + dev_err(ddev->dev, "Failed to allocate drm fbdev.\n"); + return; + } + + fb_helper = &fbdev->fb_helper; + + drm_fb_helper_prepare(ddev, fb_helper, &sdrm_fb_helper_funcs); + + ret = drm_fb_helper_init(ddev, fb_helper, 1, 1); + if (ret < 0) { + dev_err(ddev->dev, "Failed to initialize drm fb helper.\n"); + goto err_free; + } + + ret = drm_fb_helper_single_add_all_connectors(fb_helper); + if (ret < 0) { + dev_err(ddev->dev, "Failed to add connectors.\n"); + goto err_drm_fb_helper_fini; + } + + ret = drm_fb_helper_initial_config(fb_helper, + ddev->mode_config.preferred_depth); + if (ret < 0) { + dev_err(ddev->dev, "Failed to set initial hw configuration.\n"); + goto err_drm_fb_helper_fini; + } + + sdrm->fbdev = fbdev; + + return; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(fb_helper); +err_free: + kfree(fbdev); +} + +void sdrm_fbdev_cleanup(struct sdrm_device *sdrm) +{ + struct sdrm_fbdev *fbdev = sdrm->fbdev; + struct drm_fb_helper *fb_helper; + + if (!fbdev) + return; + + sdrm->fbdev = NULL; + fb_helper = &fbdev->fb_helper; + + drm_fb_helper_unregister_fbi(fb_helper); + drm_fb_helper_release_fbi(fb_helper); + + drm_framebuffer_unregister_private(&fbdev->fb); + drm_framebuffer_cleanup(&fbdev->fb); + + drm_fb_helper_fini(fb_helper); + kfree(fbdev); +} + +static void sdrm_fbdev_set_suspend(struct fb_info *fbi, int state) +{ + console_lock(); + fb_set_suspend(fbi, state); + console_unlock(); +} + +/* + * Since fbdev is using the native framebuffer, fbcon has to be disabled + * when the drm stack is used. + */ +void sdrm_fbdev_display_pipe_update(struct sdrm_device *sdrm, + struct drm_framebuffer *fb) +{ + struct sdrm_fbdev *fbdev = sdrm->fbdev; + + if (!fbdev || fbdev->fb_helper.fb == fb) + return; + + if (fbdev->fb_helper.fbdev->state == FBINFO_STATE_RUNNING) + sdrm_fbdev_set_suspend(fbdev->fb_helper.fbdev, 1); +} + +void sdrm_fbdev_restore_mode(struct sdrm_device *sdrm) +{ + struct sdrm_fbdev *fbdev = sdrm->fbdev; + + if (!fbdev) + return; + + drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->fb_helper); + + if (fbdev->fb_helper.fbdev->state != FBINFO_STATE_RUNNING) + sdrm_fbdev_set_suspend(fbdev->fb_helper.fbdev, 0); +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c index 00e50d9..92ddc116 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_kms.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c @@ -24,6 +24,13 @@ static const uint32_t sdrm_formats[] = { DRM_FORMAT_XRGB8888, }; +void sdrm_lastclose(struct drm_device *ddev) +{ + struct sdrm_device *sdrm = ddev->dev_private; + + sdrm_fbdev_restore_mode(sdrm); +} + static int sdrm_conn_get_modes(struct drm_connector *conn) { struct sdrm_device *sdrm = conn->dev->dev_private; @@ -91,8 +98,9 @@ void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe, struct sdrm_device *sdrm = pipe_to_sdrm(pipe); sdrm_crtc_send_vblank_event(&pipe->crtc); + sdrm_fbdev_display_pipe_update(sdrm, fb); - if (fb) { + if (fb && fb->funcs->dirty) { pipe->plane.fb = fb; sdrm_dirty_all_locked(sdrm); }