From patchwork Tue Mar 26 09:17:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870685 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A179E1669 for ; Tue, 26 Mar 2019 09:17:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8EDB02902D for ; Tue, 26 Mar 2019 09:17:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8234829035; Tue, 26 Mar 2019 09:17:54 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 085BB2902D for ; Tue, 26 Mar 2019 09:17:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726307AbfCZJRx (ORCPT ); Tue, 26 Mar 2019 05:17:53 -0400 Received: from mx2.suse.de ([195.135.220.15]:39698 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726261AbfCZJRx (ORCPT ); Tue, 26 Mar 2019 05:17:53 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 98025AD65; Tue, 26 Mar 2019 09:17:51 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 01/11] drm/fbdevdrm: Add driver skeleton Date: Tue, 26 Mar 2019 10:17:34 +0100 Message-Id: <20190326091744.11542-2-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The fbdevdrm driver runs DRM on top of fbdev framebuffer drivers. It allows for using legacy drivers with modern userspace and gives a template for converting fbdev drivers to DRM. Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/fbdevdrm/Kconfig | 13 +++++ drivers/gpu/drm/fbdevdrm/Makefile | 4 ++ drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c | 72 +++++++++++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 drivers/gpu/drm/fbdevdrm/Kconfig create mode 100644 drivers/gpu/drm/fbdevdrm/Makefile create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 5e1bc630b885..c7fb1d382b2c 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -337,6 +337,8 @@ source "drivers/gpu/drm/xen/Kconfig" source "drivers/gpu/drm/vboxvideo/Kconfig" +source "drivers/gpu/drm/fbdevdrm/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index e630eccb951c..ecfa0c0b7330 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -111,3 +111,4 @@ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ obj-$(CONFIG_DRM_XEN) += xen/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ +obj-$(CONFIG_DRM_FBDEVDRM) += fbdevdrm/ diff --git a/drivers/gpu/drm/fbdevdrm/Kconfig b/drivers/gpu/drm/fbdevdrm/Kconfig new file mode 100644 index 000000000000..ce071d0f4116 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/Kconfig @@ -0,0 +1,13 @@ +config DRM_FBDEVDRM + tristate "DRM over FBDEV" + depends on DRM && PCI && AGP + select FB_NOTIFY + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_TTM + help + Choose this option to use fbdev video drivers within DRM. + If M is selected the module will be called fbdevdrm. diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile new file mode 100644 index 000000000000..65e6b43cf682 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -0,0 +1,4 @@ +ccflags-y = -Iinclude/drm +fbdevdrm-y := fbdevdrm_drv.o + +obj-$(CONFIG_DRM_FBDEVDRM) += fbdevdrm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c new file mode 100644 index 000000000000..dcb263b0c386 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include +#include + +/* DRM porting note: Here are some general information about the driver, + * licensing and maintenance contact. If you're porting an fbdev driver + * to DRM, update them with the appropriate values. For the license, you + * should either pick the license of the ported fbdev driver or + * GPL-2.0-or-later. Don't forget to remove the license exception statement + * from the file headers. + */ +#define DRIVER_AUTHOR "Thomas Zimmermann " +#define DRIVER_NAME "fbdevdrm" +#define DRIVER_DESCRIPTION "DRM over FBDEV" +#define DRIVER_LICENSE "GPL and additional rights" +#define DRIVER_DATE "20190301" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 1 + +/* Module entry points */ + +static int fb_client_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + static int (* const on_event[])(struct fb_info*, void*) = { + }; + + const struct fb_event *event = data; + + if ((action >= ARRAY_SIZE(on_event)) || !on_event[action]) + return 0; /* event not handled by us */ + return on_event[action](event->info, event->data); +} + +static struct notifier_block fb_client = { + .notifier_call = fb_client_notifier_call +}; + +static int __init fbdevdrm_init(void) +{ + int ret; + + ret = fb_register_client(&fb_client); + if (ret < 0) + return ret; + + return 0; +} + +static void __exit fbdevdrm_exit(void) +{ + fb_unregister_client(&fb_client); +} + +module_init(fbdevdrm_init); +module_exit(fbdevdrm_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_LICENSE(DRIVER_LICENSE); From patchwork Tue Mar 26 09:17:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870687 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9800A1669 for ; Tue, 26 Mar 2019 09:17:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 849232902D for ; Tue, 26 Mar 2019 09:17:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 78DFA2902E; Tue, 26 Mar 2019 09:17:55 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9EF3529036 for ; Tue, 26 Mar 2019 09:17:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726367AbfCZJRy (ORCPT ); Tue, 26 Mar 2019 05:17:54 -0400 Received: from mx2.suse.de ([195.135.220.15]:39712 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726258AbfCZJRy (ORCPT ); Tue, 26 Mar 2019 05:17:54 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id A549DAD95; Tue, 26 Mar 2019 09:17:52 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device Date: Tue, 26 Mar 2019 10:17:35 +0100 Message-Id: <20190326091744.11542-3-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP There's an fbdevdrm device for each supported fbdev device. The fbdevdrm driver listens for fb events, and creates and destroys fbdevdrm devices accordingly. Only hardware-specific fbdev drivers are supported. Generic drivers, such as vga16fb or vesafb are not handled. DRM-based fbdev drivers are also not supported. This prevents the situation where a DRM drivers enables framebuffer emulation and fbdevdrm creates a second DRM device for the same hardware. Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/Makefile | 3 +- drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c | 79 ++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h | 44 ++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c | 173 ++++++++++++++++++++- 4 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile index 65e6b43cf682..750940d38509 100644 --- a/drivers/gpu/drm/fbdevdrm/Makefile +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -1,4 +1,5 @@ ccflags-y = -Iinclude/drm -fbdevdrm-y := fbdevdrm_drv.o +fbdevdrm-y := fbdevdrm_device.o \ + fbdevdrm_drv.o obj-$(CONFIG_DRM_FBDEVDRM) += fbdevdrm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c new file mode 100644 index 000000000000..0abf41cf05bb --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_device.h" +#include +#include +#include +#include +#include + +/* + * struct fbdrmdev_device + */ + +int fbdevdrm_device_init(struct fbdevdrm_device *fdev, struct drm_driver *drv, + struct fb_info *fb_info) +{ + int ret; + + ret = drm_dev_init(&fdev->dev, drv, fb_info->device); + if (ret) + return ret; + fdev->dev.dev_private = fdev; + fdev->dev.pdev = container_of(fb_info->device, struct pci_dev, dev); + fdev->fb_info = fb_info; + + INIT_LIST_HEAD(&fdev->device_list); + + return 0; +} + +void fbdevdrm_device_cleanup(struct fbdevdrm_device *fdev) +{ + struct drm_device *dev = &fdev->dev; + + if (!list_empty(&fdev->device_list)) { + DRM_ERROR("fbdevdrm: cleaned up device is still enqueued " + "in device list\n"); + } + + drm_dev_fini(dev); + dev->dev_private = NULL; +} + +struct fbdevdrm_device* fbdevdrm_device_create(struct drm_driver *drv, + struct fb_info *fb_info) +{ + struct fbdevdrm_device *fdev; + int ret; + + fdev = devm_kzalloc(fb_info->dev, sizeof(*fdev), GFP_KERNEL); + if (!fdev) + return ERR_PTR(-ENOMEM); + ret = fbdevdrm_device_init(fdev, drv, fb_info); + if (ret < 0) + goto err_devm_kfree; + return fdev; + +err_devm_kfree: + devm_kfree(fb_info->dev, fdev); + return ERR_PTR(ret); +} + +void fbdevdrm_device_destroy(struct fbdevdrm_device *fdev) +{ + struct device *dev = fdev->fb_info->dev; + + fbdevdrm_device_cleanup(fdev); + devm_kfree(dev, fdev); +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h new file mode 100644 index 000000000000..85878f60bba4 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_DEVICE_H +#define FBDEVDRM_DEVICE_H + +#include +#include +#include + +struct drm_driver; +struct fb_info; + +struct fbdevdrm_device { + struct drm_device dev; + struct fb_info *fb_info; + + struct list_head device_list; /* entry in global device list */ +}; + +static inline struct fbdevdrm_device* fbdevdrm_device_of_device_list( + struct list_head *device_list) +{ + return list_entry(device_list, struct fbdevdrm_device, device_list); +} + +int fbdevdrm_device_init(struct fbdevdrm_device *fdev, struct drm_driver *drv, + struct fb_info *fb_info); +void fbdevdrm_device_cleanup(struct fbdevdrm_device *fdev); + +struct fbdevdrm_device* fbdevdrm_device_create(struct drm_driver *drv, + struct fb_info *fb_info); +void fbdevdrm_device_destroy(struct fbdevdrm_device *fdev); + +#endif diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c index dcb263b0c386..5be902094dc6 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c @@ -10,8 +10,13 @@ * stated exception. */ +#include +#include +#include /* for console_{un/lock}() */ #include #include +#include +#include "fbdevdrm_device.h" /* DRM porting note: Here are some general information about the driver, * licensing and maintenance contact. If you're porting an fbdev driver @@ -29,18 +34,156 @@ #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 1 +/* + * DRM driver + */ + +static struct drm_driver fbdevdrm_drv = { + /* data fields */ + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + .name = DRIVER_NAME, + .desc = DRIVER_DESCRIPTION, + .date = DRIVER_DATE +}; + +/* Device list */ + +static DEFINE_MUTEX(device_list_mutex); +static LIST_HEAD(device_list); + +static void device_list_add(struct fbdevdrm_device *fdev) +{ + mutex_lock(&device_list_mutex); + list_add(&fdev->device_list, &device_list); + mutex_unlock(&device_list_mutex); +} + +static struct fbdevdrm_device* device_list_del(struct fbdevdrm_device *fdev) +{ + mutex_lock(&device_list_mutex); + list_del(&fdev->device_list); + mutex_unlock(&device_list_mutex); + + return fdev; +} + +static struct fbdevdrm_device* device_list_find_by_fb_info( + const struct fb_info *fb_info) +{ + struct list_head *pos; + struct fbdevdrm_device *fdev = NULL; + + mutex_lock(&device_list_mutex); + list_for_each(pos, &device_list) { + struct fbdevdrm_device *pos_fdev = + fbdevdrm_device_of_device_list(pos); + if (pos_fdev->fb_info == fb_info) { + fdev = pos_fdev; + goto out; + } + } +out: + mutex_unlock(&device_list_mutex); + return fdev; +} + /* Module entry points */ +static bool is_generic_driver(const struct fb_info *fb_info) +{ + /* DRM porting note: We don't want to bind to vga16fb, vesafb, or any + * other generic fbdev driver. Usually, these drivers have limited + * capabilitis. We only continue if the fix structure indicates a + * hardware-specific drivers . This test will also sort out drivers + * registered via DRM's fbdev emulation. If you're porting an fbdev + * driver to DRM, you can remove this test. The module's PCI device + * ids will contain this information. + */ + return !fb_info->fix.accel && + !!strcmp(fb_info->fix.id, "S3 Virge/DX"); +} + +static int on_fb_registered(struct fb_info *fb_info, void *data) +{ + struct fbdevdrm_device *fdev; + int ret; + + if (is_generic_driver(fb_info)) { + DRM_ERROR("fbdevdrm: not binding to %s\n", fb_info->fix.id); + return 0; + } + + fdev = fbdevdrm_device_create(&fbdevdrm_drv, fb_info); + if (IS_ERR(fdev)) + return PTR_ERR(fdev); + device_list_add(fdev); + + ret = drm_dev_register(&fdev->dev, 0); + if (ret) + goto err_device_list_del; + + return 0; + +err_device_list_del: + device_list_del(fdev); + fbdevdrm_device_destroy(fdev); + return ret; +} + +static int on_fb_unregistered(struct fb_info *fb_info, void *data) +{ + struct fbdevdrm_device *fdev; + + fdev = device_list_find_by_fb_info(fb_info); + if (!fdev) + return 0; + + device_list_del(fdev); + fbdevdrm_device_destroy(fdev); + + return 0; +} + static int fb_client_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { + static const char* const event_name[] = { +#define EVENT_NAME(_ev) \ + [_ev] = #_ev + EVENT_NAME(FB_EVENT_MODE_CHANGE), + EVENT_NAME(FB_EVENT_SUSPEND), + EVENT_NAME(FB_EVENT_RESUME), + EVENT_NAME(FB_EVENT_MODE_DELETE), + EVENT_NAME(FB_EVENT_FB_REGISTERED), + EVENT_NAME(FB_EVENT_FB_UNREGISTERED), + EVENT_NAME(FB_EVENT_GET_CONSOLE_MAP), + EVENT_NAME(FB_EVENT_SET_CONSOLE_MAP), + EVENT_NAME(FB_EVENT_BLANK), + EVENT_NAME(FB_EVENT_NEW_MODELIST), + EVENT_NAME(FB_EVENT_MODE_CHANGE_ALL), + EVENT_NAME(FB_EVENT_CONBLANK), + EVENT_NAME(FB_EVENT_GET_REQ), + EVENT_NAME(FB_EVENT_FB_UNBIND), + EVENT_NAME(FB_EVENT_REMAP_ALL_CONSOLE), + EVENT_NAME(FB_EARLY_EVENT_BLANK), + EVENT_NAME(FB_R_EARLY_EVENT_BLANK) +#undef EVENT_NAME + }; + static int (* const on_event[])(struct fb_info*, void*) = { + [FB_EVENT_FB_REGISTERED] = on_fb_registered, + [FB_EVENT_FB_UNREGISTERED] = on_fb_unregistered }; const struct fb_event *event = data; - if ((action >= ARRAY_SIZE(on_event)) || !on_event[action]) + if ((action >= ARRAY_SIZE(on_event)) || !on_event[action]) { + DRM_ERROR("fbdevdrm: unhandled event %s\n", + event_name[action]); return 0; /* event not handled by us */ + } return on_event[action](event->info, event->data); } @@ -50,13 +193,39 @@ static struct notifier_block fb_client = { static int __init fbdevdrm_init(void) { - int ret; + int ret, i; ret = fb_register_client(&fb_client); if (ret < 0) return ret; + /* There might already be registered FB devices. We go + * through them manually to create a corresponding DRM + * device. For the event notifier, all the locking is + * performed by the fbdev framework. Here, we have to + * do it by ourselves. */ + + console_lock(); + + for_each_registered_fb(i) { + struct fb_info *fb_info = registered_fb[i]; + if (!lock_fb_info(fb_info)) { + ret = -ENODEV; + goto err_console_unlock; + } + ret = on_fb_registered(fb_info, NULL); + unlock_fb_info(fb_info); + if (ret < 0) + goto err_console_unlock; + } + + console_unlock(); + return 0; + +err_console_unlock: + console_unlock(); + return ret; } static void __exit fbdevdrm_exit(void) From patchwork Tue Mar 26 09:17:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870693 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1F8FA186E for ; Tue, 26 Mar 2019 09:17:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0A6FD2902D for ; Tue, 26 Mar 2019 09:17:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F302229036; Tue, 26 Mar 2019 09:17:56 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B1F222902D for ; Tue, 26 Mar 2019 09:17:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726413AbfCZJRz (ORCPT ); Tue, 26 Mar 2019 05:17:55 -0400 Received: from mx2.suse.de ([195.135.220.15]:39724 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726261AbfCZJRz (ORCPT ); Tue, 26 Mar 2019 05:17:55 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 960B7ADC4; Tue, 26 Mar 2019 09:17:53 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 03/11] drm/fbdevdrm: Add memory management Date: Tue, 26 Mar 2019 10:17:36 +0100 Message-Id: <20190326091744.11542-4-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Memory management in fbdevdrm is provided by TTM. Fbdev implements page flipping via display pan operations, which require scanout buffers to be aligned at scanline boundaries. At the same time memory-mapping operations of individual buffers requires page alignment of each buffer. Fbdevdrm ensures that both requirements are met. Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/Makefile | 6 +- drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c | 276 +++++++++++++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h | 58 +++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c | 10 + drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h | 9 + drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c | 202 +++++++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h | 35 +++ 7 files changed, 594 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile index 750940d38509..fdfdb5233831 100644 --- a/drivers/gpu/drm/fbdevdrm/Makefile +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -1,5 +1,7 @@ ccflags-y = -Iinclude/drm -fbdevdrm-y := fbdevdrm_device.o \ - fbdevdrm_drv.o +fbdevdrm-y := fbdevdrm_bo.o \ + fbdevdrm_device.o \ + fbdevdrm_drv.o \ + fbdevdrm_ttm.o obj-$(CONFIG_DRM_FBDEVDRM) += fbdevdrm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c new file mode 100644 index 000000000000..78370db6c504 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_bo.h" +#include "fbdevdrm_device.h" + +static void fbdevdrm_bo_cleanup(struct fbdevdrm_bo *fbo) +{ + drm_gem_object_release(&fbo->gem); +} + +static void fbdevdrm_bo_destroy(struct fbdevdrm_bo *fbo) +{ + /* We got here via ttm_bo_put(), which means that the + * TTM buffer object in 'bo' has already been cleaned + * up. */ + + fbdevdrm_bo_cleanup(fbo); + kfree(fbo); +} + +static void fbdevdrm_bo_ttm_destroy(struct ttm_buffer_object *bo) +{ + struct fbdevdrm_bo *fbo = fbdevdrm_bo_of_bo(bo); + fbdevdrm_bo_destroy(fbo); +} + +static void fbdevdrm_bo_ttm_placement(struct fbdevdrm_bo *bo, int pl_flag) +{ + unsigned int i; + unsigned int c = 0; + + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + + if (pl_flag & TTM_PL_FLAG_VRAM) + bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + + if (pl_flag & TTM_PL_FLAG_SYSTEM) + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + if (!c) + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + bo->placement.num_placement = c; + bo->placement.num_busy_placement = c; + + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + } +} + +static int fbdevdrm_bo_init(struct fbdevdrm_bo *fbo, struct drm_device *dev, + int size, int align, uint32_t flags) +{ + struct fbdevdrm_device *fdev; + int ret; + size_t acc_size; + + fdev = fbdevdrm_device_of_dev(dev); + + ret = drm_gem_object_init(dev, &fbo->gem, size); + if (ret < 0) + return ret; + + fbo->bo.bdev = &fdev->ttm.bdev; + + fbdevdrm_bo_ttm_placement(fbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + acc_size = ttm_bo_dma_acc_size(&fdev->ttm.bdev, size, sizeof(*fbo)); + + ret = ttm_bo_init(&fdev->ttm.bdev, &fbo->bo, size, + ttm_bo_type_device, &fbo->placement, + align >> PAGE_SHIFT, false, acc_size, + NULL, NULL, fbdevdrm_bo_ttm_destroy); + if (ret < 0) + goto err_drm_gem_object_release; + + return 0; + +err_drm_gem_object_release: + drm_gem_object_release(&fbo->gem); + return ret; +} + +/* Returns the least common multiple of a value and the page size + */ +static u32 lcm_with_page_size(u32 size) +{ + if (!size) + return size; /* 0 is always correct */ + while (size && (size & ~PAGE_MASK)) + size <<= 1; + if (!size) + return (u32)-EINVAL; /* no common multiple found */ + return size; +} + +static int fbdevdrm_bo_init_with_pitch(struct fbdevdrm_bo *fbo, + struct drm_device *dev, int size, + int pitch, uint32_t flags) +{ + u32 align = lcm_with_page_size(pitch); + if (align == (u32)-EINVAL) { + DRM_ERROR("fbdevdrm: buffer pitch of %d bytes has no " + "common multiple with page size.\n", pitch); + return -EINVAL; + } + + return fbdevdrm_bo_init(fbo, dev, size, align, flags); +} + +/* + * Public interface + */ + +struct fbdevdrm_bo* fbdevdrm_bo_create(struct drm_device *dev, int size, + int align, uint32_t flags) +{ + struct fbdevdrm_bo *fbo; + int ret; + + fbo = kzalloc(sizeof(*fbo), GFP_KERNEL); + if (!fbo) + return ERR_PTR(-ENOMEM); + + ret = fbdevdrm_bo_init(fbo, dev, size, align, flags); + if (ret < 0) + goto err_kfree; + + return fbo; + +err_kfree: + kfree(fbo); + return ERR_PTR(ret); +} + +struct fbdevdrm_bo* fbdevdrm_bo_create_with_pitch(struct drm_device *dev, + int size, int pitch, + uint32_t flags) +{ + struct fbdevdrm_bo *fbo; + int ret; + + fbo = kzalloc(sizeof(*fbo), GFP_KERNEL); + if (!fbo) + return ERR_PTR(-ENOMEM); + + ret = fbdevdrm_bo_init_with_pitch(fbo, dev, size, pitch, flags); + if (ret < 0) + goto err_kfree; + + return fbo; + +err_kfree: + kfree(fbo); + return ERR_PTR(ret); +} + +void fbdevdrm_bo_put(struct fbdevdrm_bo *fbo) +{ + ttm_bo_put(&fbo->bo); +} + +int fbdevdrm_bo_reserve(struct fbdevdrm_bo *fbo, bool no_wait) +{ + int ret; + + ret = ttm_bo_reserve(&fbo->bo, true, no_wait, NULL); + if (ret < 0) { + if (ret != -ERESTARTSYS && ret != -EBUSY) + DRM_ERROR("fbdevdrm: ttm_bo_reserve(%p) failed," + " error %d\n", fbo, -ret); + return ret; + } + return 0; +} + +void fbdevdrm_bo_unreserve(struct fbdevdrm_bo *fbo) +{ + ttm_bo_unreserve(&fbo->bo); +} + +u64 fbdevdrm_bo_mmap_offset(struct fbdevdrm_bo *fbo) +{ + return drm_vma_node_offset_addr(&fbo->bo.vma_node); +} + +int fbdevdrm_bo_pin(struct fbdevdrm_bo *fbo, u32 pl_flag) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (fbo->pin_count) { + ++fbo->pin_count; + return 0; + } + + fbdevdrm_bo_ttm_placement(fbo, pl_flag); + for (i = 0; i < fbo->placement.num_placement; ++i) + fbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&fbo->bo, &fbo->placement, &ctx); + if (ret < 0) { + DRM_ERROR("fbdevdrm: ttm_bo_validate failed, error %d\n", -ret); + return ret; + } + + fbo->pin_count = 1; + + return 0; +} + +int fbdevdrm_bo_unpin(struct fbdevdrm_bo *fbo) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (!fbo->pin_count) { + DRM_ERROR("fbdevdrm: BO %p is not pinned \n", fbo); + return 0; + } + --fbo->pin_count; + if (fbo->pin_count) + return 0; + + for (i = 0; i < fbo->placement.num_placement ; ++i) + fbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&fbo->bo, &fbo->placement, &ctx); + if (ret < 0) { + DRM_ERROR("fbdevdrm: ttm_bo_validate failed, error %d\n", -ret); + return ret; + } + + return 0; +} + +int fbdevdrm_bo_push_to_system(struct fbdevdrm_bo *fbo) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (!fbo->pin_count) { + DRM_ERROR("fbdevdrm: BO %p is not pinned \n", fbo); + return 0; + } + --fbo->pin_count; + if (fbo->pin_count) + return 0; + + if (fbo->kmap.virtual) + ttm_bo_kunmap(&fbo->kmap); + + fbdevdrm_bo_ttm_placement(fbo, TTM_PL_FLAG_SYSTEM); + for (i = 0; i < fbo->placement.num_placement ; ++i) + fbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&fbo->bo, &fbo->placement, &ctx); + if (ret) { + DRM_ERROR("fbdevdrm: ttm_bo_validate failed, error %d\n", -ret); + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h new file mode 100644 index 000000000000..7602a0fbd6a5 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_BO_H +#define FBDEVDRM_BO_H + +#include +#include +#include +#include /* for container_of() */ + +struct fbdevdrm_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct drm_gem_object gem; + + /* Supported placements are VRAM and SYSTEM */ + struct ttm_place placements[3]; + int pin_count; +}; + +static inline struct fbdevdrm_bo* fbdevdrm_bo_of_bo( + struct ttm_buffer_object *bo) +{ + return container_of(bo, struct fbdevdrm_bo, bo); +} + +static inline struct fbdevdrm_bo* fbdevdrm_bo_of_gem( + struct drm_gem_object *gem) +{ + return container_of(gem, struct fbdevdrm_bo, gem); +} + +struct fbdevdrm_bo* fbdevdrm_bo_create(struct drm_device *dev, int size, + int align, uint32_t flags); +struct fbdevdrm_bo* fbdevdrm_bo_create_with_pitch(struct drm_device *dev, + int size, int pitch, + uint32_t flags); +void fbdevdrm_bo_put(struct fbdevdrm_bo *fbo); + +int fbdevdrm_bo_reserve(struct fbdevdrm_bo *fbo, bool no_wait); +void fbdevdrm_bo_unreserve(struct fbdevdrm_bo *fbo); +u64 fbdevdrm_bo_mmap_offset(struct fbdevdrm_bo *fbo); +int fbdevdrm_bo_pin(struct fbdevdrm_bo *fbo, u32 pl_flag); +int fbdevdrm_bo_unpin(struct fbdevdrm_bo *fbo); +int fbdevdrm_bo_push_to_system(struct fbdevdrm_bo *fbo); + +#endif diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c index 0abf41cf05bb..c8054eac271d 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c @@ -33,9 +33,17 @@ int fbdevdrm_device_init(struct fbdevdrm_device *fdev, struct drm_driver *drv, fdev->dev.pdev = container_of(fb_info->device, struct pci_dev, dev); fdev->fb_info = fb_info; + ret = fbdevdrm_ttm_init(&fdev->ttm, &fdev->dev, fb_info); + if (ret) + goto err_drm_dev_fini; + INIT_LIST_HEAD(&fdev->device_list); return 0; + +err_drm_dev_fini: + drm_dev_fini(&fdev->dev); + return ret; } void fbdevdrm_device_cleanup(struct fbdevdrm_device *fdev) @@ -47,6 +55,8 @@ void fbdevdrm_device_cleanup(struct fbdevdrm_device *fdev) "in device list\n"); } + fbdevdrm_ttm_cleanup(&fdev->ttm); + drm_dev_fini(dev); dev->dev_private = NULL; } diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h index 85878f60bba4..381d9cfb1450 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h @@ -16,6 +16,7 @@ #include #include #include +#include "fbdevdrm_ttm.h" struct drm_driver; struct fb_info; @@ -24,6 +25,8 @@ struct fbdevdrm_device { struct drm_device dev; struct fb_info *fb_info; + struct fbdevdrm_ttm ttm; + struct list_head device_list; /* entry in global device list */ }; @@ -33,6 +36,12 @@ static inline struct fbdevdrm_device* fbdevdrm_device_of_device_list( return list_entry(device_list, struct fbdevdrm_device, device_list); } +static inline struct fbdevdrm_device* fbdevdrm_device_of_dev( + struct drm_device *dev) +{ + return container_of(dev, struct fbdevdrm_device, dev); +} + int fbdevdrm_device_init(struct fbdevdrm_device *fdev, struct drm_driver *drv, struct fb_info *fb_info); void fbdevdrm_device_cleanup(struct fbdevdrm_device *fdev); diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c new file mode 100644 index 000000000000..f7aeff755c30 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_ttm.h" +#include +#include +#include "fbdevdrm_bo.h" + +static struct fbdevdrm_ttm* fbdevdrm_ttm_of_bdev( + struct ttm_bo_device *bdev) +{ + return container_of(bdev, struct fbdevdrm_ttm, bdev); +} + +/* + * TTM BO device + */ + +static void fbdevdrm_ttm_backend_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func fbdevdrm_ttm_backend_func = { + .destroy = fbdevdrm_ttm_backend_destroy +}; + +static struct ttm_tt *fbdevdrm_ttm_tt_create(struct ttm_buffer_object *bo, + uint32_t page_flags) +{ + struct ttm_tt *tt; + int ret; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) + return NULL; + + tt->func = &fbdevdrm_ttm_backend_func; + + ret = ttm_tt_init(tt, bo, page_flags); + if (ret < 0) + goto err_ttm_tt_init; + + return tt; + +err_ttm_tt_init: + kfree(tt); + return NULL; +} + +static int fbdevdrm_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("fbdevdrm: Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void fbdevdrm_bo_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *pl) +{ } + +static int fbdevdrm_bo_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + struct fbdevdrm_bo *fbo = fbdevdrm_bo_of_bo(bo); + + return drm_vma_node_verify_access(&fbo->gem.vma_node, + filp->private_data); +} + +static int fbdevdrm_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = bdev->man + mem->mem_type; + struct fbdevdrm_ttm *ttm = fbdevdrm_ttm_of_bdev(bdev); + + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + + mem->bus.addr = NULL; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: /* nothing to do */ + mem->bus.offset = 0; + mem->bus.base = 0; + mem->bus.is_iomem = false; + break; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = ttm->fb_info->fix.smem_start; + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void fbdevdrm_ttm_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ } + +static struct ttm_bo_driver fbdevdrm_bo_driver = { + .ttm_tt_create = fbdevdrm_ttm_tt_create, + .ttm_tt_populate = ttm_pool_populate, + .ttm_tt_unpopulate = ttm_pool_unpopulate, + .init_mem_type = fbdevdrm_bo_init_mem_type, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = fbdevdrm_bo_evict_flags, + .verify_access = fbdevdrm_bo_verify_access, + .io_mem_reserve = fbdevdrm_ttm_io_mem_reserve, + .io_mem_free = fbdevdrm_ttm_io_mem_free, +}; + +static int fbdevdrm_init_ttm_bo_device(struct fbdevdrm_ttm *ttm, + struct drm_device *dev, + unsigned long p_size) +{ + int ret; + + ret = ttm_bo_device_init(&ttm->bdev, + &fbdevdrm_bo_driver, + dev->anon_inode->i_mapping, + DRM_FILE_PAGE_OFFSET, + true); + if (ret) { + DRM_ERROR("fbdevdrm: ttm_bo_device_init failed; %d\n", ret); + return ret; + } + + ret = ttm_bo_init_mm(&ttm->bdev, TTM_PL_VRAM, p_size); + if (ret) { + DRM_ERROR("fbdevdrm: ttm_bo_init_mm failed: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * struct fbdevdrm_ttm + */ + +int fbdevdrm_ttm_init(struct fbdevdrm_ttm *ttm, struct drm_device *dev, + struct fb_info *fb_info) +{ + int ret; + + ttm->dev = dev; + ttm->fb_info = fb_info; + + /* DRM porting notes: programming the linear scanout buffer is + * implemented via display panning. To make this work, all BOs + * have to end at scanline boundaries. The final BO could be placed + * at the very end of the display memory, which might not align + * with the end of the final scanline. Fbdev would reject such a + * coordinate for display panning. To avoid this, we don't use the + * display memory's final 4 pages. If you convert an fbdev driver + * to DRM, remove this workaround and hand-over all memory to TTM. + */ + ttm->vram_size = fb_info->fix.smem_len - 4 * PAGE_SIZE; + + ret = fbdevdrm_init_ttm_bo_device(ttm, dev, ttm->vram_size >> PAGE_SHIFT); + if (ret < 0) + return ret; + + return 0; +} + +void fbdevdrm_ttm_cleanup(struct fbdevdrm_ttm *ttm) +{ + ttm_bo_device_release(&ttm->bdev); +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h new file mode 100644 index 000000000000..d3e964cd8215 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_TTM_H +#define FBDEVDRM_TTM_H + +#include + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +struct drm_device; +struct fb_info; + +struct fbdevdrm_ttm { + struct drm_device *dev; + struct fb_info *fb_info; + + size_t vram_size; + struct ttm_bo_device bdev; +}; + +int fbdevdrm_ttm_init(struct fbdevdrm_ttm *ttm, struct drm_device *dev, + struct fb_info *fb_info); +void fbdevdrm_ttm_cleanup(struct fbdevdrm_ttm *ttm); + +#endif From patchwork Tue Mar 26 09:17:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870689 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8A5871669 for ; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7862C2902E for ; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6CFC129037; Tue, 26 Mar 2019 09:17:56 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0C9622902E for ; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726261AbfCZJRz (ORCPT ); Tue, 26 Mar 2019 05:17:55 -0400 Received: from mx2.suse.de ([195.135.220.15]:39740 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726258AbfCZJRz (ORCPT ); Tue, 26 Mar 2019 05:17:55 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 6A23BAE15; Tue, 26 Mar 2019 09:17:54 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 04/11] drm/fbdevdrm: Add file operations Date: Tue, 26 Mar 2019 10:17:37 +0100 Message-Id: <20190326091744.11542-5-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c | 29 ++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c index 5be902094dc6..4a6ba6c85c5c 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c @@ -11,6 +11,8 @@ */ #include +#include +#include #include #include /* for console_{un/lock}() */ #include @@ -38,6 +40,30 @@ * DRM driver */ +static int driver_fops_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct fbdevdrm_device *fdev = file_priv->minor->dev->dev_private; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return -EINVAL; + + return ttm_bo_mmap(filp, vma, &fdev->ttm.bdev); +} + +static const struct file_operations driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = driver_fops_mmap, + .poll = drm_poll, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .read = drm_read +}; + static struct drm_driver fbdevdrm_drv = { /* data fields */ .major = DRIVER_MAJOR, @@ -45,7 +71,8 @@ static struct drm_driver fbdevdrm_drv = { .patchlevel = DRIVER_PATCHLEVEL, .name = DRIVER_NAME, .desc = DRIVER_DESCRIPTION, - .date = DRIVER_DATE + .date = DRIVER_DATE, + .fops = &driver_fops }; /* Device list */ From patchwork Tue Mar 26 09:17:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870695 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3D03517E6 for ; Tue, 26 Mar 2019 09:17:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2A0822902D for ; Tue, 26 Mar 2019 09:17:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1E2762903E; Tue, 26 Mar 2019 09:17:57 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A16E72902E for ; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728991AbfCZJR4 (ORCPT ); Tue, 26 Mar 2019 05:17:56 -0400 Received: from mx2.suse.de ([195.135.220.15]:39724 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726258AbfCZJR4 (ORCPT ); Tue, 26 Mar 2019 05:17:56 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 6F89AACD0; Tue, 26 Mar 2019 09:17:55 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 05/11] drm/fbdevdrm: Add GEM and dumb interfaces Date: Tue, 26 Mar 2019 10:17:38 +0100 Message-Id: <20190326091744.11542-6-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c index 4a6ba6c85c5c..4724e3df6ace 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c @@ -18,6 +18,7 @@ #include #include #include +#include "fbdevdrm_bo.h" #include "fbdevdrm_device.h" /* DRM porting note: Here are some general information about the driver, @@ -64,7 +65,82 @@ static const struct file_operations driver_fops = { .read = drm_read }; +static void driver_gem_free_object(struct drm_gem_object *gobj) +{ + struct fbdevdrm_bo *fbo = fbdevdrm_bo_of_gem(gobj); + fbdevdrm_bo_put(fbo); +} + +static int driver_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int ret; + struct fbdevdrm_bo *fbo; + u32 size, handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + size = roundup(args->size, PAGE_SIZE); + if (!size) + return -EINVAL; + + /* DRM porting note: FBdev aligns scanout buffers to multiples + * of the scanline size in bytes. TTM aligns buffers to page + * boundaries. To maintain FBdev buffers with TTM, we align BOs + * at both, the scanline offsets and the page offsets. Depending + * on resolution and color depth, this may result in some memory + * overhead. After porting an FBdev driver to DRM, you can remove + * this constrain and simply align to page boundaries. + */ + fbo = fbdevdrm_bo_create_with_pitch(dev, size, args->pitch, 0); + if (IS_ERR(fbo)) { + ret = PTR_ERR(fbo); + if (ret != -ERESTARTSYS) + DRM_ERROR("fbdevdrm: fbdevdrm_bo_create_with_pitch() " + "failed, error %d\n", -ret); + return ret; + } + + ret = drm_gem_handle_create(file_priv, &fbo->gem, &handle); + drm_gem_object_put_unlocked(&fbo->gem); /* TODO: verify ref-count */ + if (ret < 0) + goto err_fbdevdrm_bo_put; + + args->handle = handle; + + return 0; + +err_fbdevdrm_bo_put: + fbdevdrm_bo_put(fbo); + return ret; +} + +static int driver_dumb_mmap_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + struct fbdevdrm_bo *fbo; + + obj = drm_gem_object_lookup(file_priv, handle); + if (obj == NULL) + return -ENOENT; + + fbo = fbdevdrm_bo_of_gem(obj); + *offset = fbdevdrm_bo_mmap_offset(fbo); + + drm_gem_object_put_unlocked(obj); + return 0; +} + static struct drm_driver fbdevdrm_drv = { + /* GEM interfaces */ + .gem_free_object = driver_gem_free_object, + /* Dumb interfaces */ + .dumb_create = driver_dumb_create, + .dumb_map_offset = driver_dumb_mmap_offset, /* data fields */ .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, @@ -72,6 +148,7 @@ static struct drm_driver fbdevdrm_drv = { .name = DRIVER_NAME, .desc = DRIVER_DESCRIPTION, .date = DRIVER_DATE, + .driver_features = DRIVER_GEM, .fops = &driver_fops }; From patchwork Tue Mar 26 09:17:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870697 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 277BA15AC for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 145912902D for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 08DA629037; Tue, 26 Mar 2019 09:17:59 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E1D0C2902D for ; Tue, 26 Mar 2019 09:17:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730637AbfCZJR5 (ORCPT ); Tue, 26 Mar 2019 05:17:57 -0400 Received: from mx2.suse.de ([195.135.220.15]:39740 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727956AbfCZJR5 (ORCPT ); Tue, 26 Mar 2019 05:17:57 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id B0C05ACEB; Tue, 26 Mar 2019 09:17:55 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 06/11] drm/fbdevdrm: Add modesetting infrastructure Date: Tue, 26 Mar 2019 10:17:39 +0100 Message-Id: <20190326091744.11542-7-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Modesetting for fbdevdrm supports a single display pipeline with CRTC, primary plane, encoder and connector. The fbdev device would have been an ideal candidate for using |struct drm_simple_display_pipe|. To better illustrate the conversion from fbdev to DRM drivers, fbdevdrm used the regular DRM data structures instead. Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/Makefile | 1 + drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c | 7 + drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h | 2 + drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c | 4 +- drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 430 ++++++++++++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h | 36 ++ 6 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile index fdfdb5233831..b8fab9d52faa 100644 --- a/drivers/gpu/drm/fbdevdrm/Makefile +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -2,6 +2,7 @@ ccflags-y = -Iinclude/drm fbdevdrm-y := fbdevdrm_bo.o \ fbdevdrm_device.o \ fbdevdrm_drv.o \ + fbdevdrm_modeset.o \ fbdevdrm_ttm.o obj-$(CONFIG_DRM_FBDEVDRM) += fbdevdrm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c index c8054eac271d..bb034f3d9392 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c @@ -37,10 +37,16 @@ int fbdevdrm_device_init(struct fbdevdrm_device *fdev, struct drm_driver *drv, if (ret) goto err_drm_dev_fini; + ret = fbdevdrm_modeset_init(&fdev->modeset, &fdev->dev, fb_info); + if (ret) + goto err_fbdevdrm_ttm_cleanup; + INIT_LIST_HEAD(&fdev->device_list); return 0; +err_fbdevdrm_ttm_cleanup: + fbdevdrm_ttm_cleanup(&fdev->ttm); err_drm_dev_fini: drm_dev_fini(&fdev->dev); return ret; @@ -55,6 +61,7 @@ void fbdevdrm_device_cleanup(struct fbdevdrm_device *fdev) "in device list\n"); } + fbdevdrm_modeset_cleanup(&fdev->modeset); fbdevdrm_ttm_cleanup(&fdev->ttm); drm_dev_fini(dev); diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h index 381d9cfb1450..4068d12e3270 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h @@ -16,6 +16,7 @@ #include #include #include +#include "fbdevdrm_modeset.h" #include "fbdevdrm_ttm.h" struct drm_driver; @@ -26,6 +27,7 @@ struct fbdevdrm_device { struct fb_info *fb_info; struct fbdevdrm_ttm ttm; + struct fbdevdrm_modeset modeset; struct list_head device_list; /* entry in global device list */ }; diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c index 4724e3df6ace..dff9f44f05bd 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c @@ -148,7 +148,9 @@ static struct drm_driver fbdevdrm_drv = { .name = DRIVER_NAME, .desc = DRIVER_DESCRIPTION, .date = DRIVER_DATE, - .driver_features = DRIVER_GEM, + .driver_features = DRIVER_ATOMIC | + DRIVER_GEM | + DRIVER_MODESET, .fops = &driver_fops }; diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c new file mode 100644 index 000000000000..585f3478f190 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c @@ -0,0 +1,430 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_modeset.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * CRTC + */ + +static enum drm_mode_status crtc_helper_mode_valid( + struct drm_crtc *crtc, const struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static bool crtc_helper_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void crtc_helper_mode_set_nofb(struct drm_crtc *crtc) +{ } + +static int crtc_helper_mode_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, + enum mode_set_atomic mode) +{ + return 0; +} + +static int crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return 0; +} + +static void crtc_helper_atomic_begin( + struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) +{ } + +static void crtc_helper_atomic_flush( + struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) +{ } + +static void crtc_helper_atomic_enable( + struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) +{ } + +static void crtc_helper_atomic_disable( + struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) +{ } + +static const struct drm_crtc_helper_funcs fbdevdrm_crtc_helper_funcs = { + .dpms = NULL, /* legacy */ + .prepare = NULL, /* legacy */ + .commit = NULL, /* legacy */ + .mode_valid = crtc_helper_mode_valid, + .mode_fixup = crtc_helper_mode_fixup, + .mode_set = NULL, /* legacy */ + .mode_set_nofb = crtc_helper_mode_set_nofb, + .mode_set_base = NULL, /* legacy */ + .mode_set_base_atomic = crtc_helper_mode_set_base_atomic, + .disable = NULL, /* legacy */ + .atomic_check = crtc_helper_atomic_check, + .atomic_begin = crtc_helper_atomic_begin, + .atomic_flush = crtc_helper_atomic_flush, + .atomic_enable = crtc_helper_atomic_enable, + .atomic_disable = crtc_helper_atomic_disable, +}; + +static void crtc_destroy(struct drm_crtc *crtc) +{ } + +static int crtc_atomic_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t val) +{ + return -EINVAL; +} + +static int crtc_atomic_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, + uint64_t *val) +{ + return -EINVAL; +} + +static int crtc_enable_vblank(struct drm_crtc *crtc) +{ + return -ENODEV; +} + +static void crtc_disable_vblank(struct drm_crtc *crtc) +{ } + +static const struct drm_crtc_funcs fbdevdrm_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .cursor_set = NULL, /* not supported by fbdev */ + .cursor_set2 = NULL, + .cursor_move = NULL, + .gamma_set = NULL, /* not supported by fbdev */ + .destroy = crtc_destroy, + .set_config = drm_atomic_helper_set_config, + .page_flip = NULL, + .page_flip_target = NULL, + .set_property = NULL, /* unused in atomic drivers */ + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_set_property = crtc_atomic_set_property, + .atomic_get_property = crtc_atomic_get_property, + .late_register = NULL, + .early_unregister = NULL, + .set_crc_source = NULL, + .atomic_print_state = NULL, + .get_vblank_counter = NULL, /* not supported by fbdev */ + .enable_vblank = crtc_enable_vblank, + .disable_vblank = crtc_disable_vblank +}; + +/* + * Encoder + */ + +static enum drm_mode_status encoder_helper_mode_valid( + struct drm_encoder *crtc, const struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static bool encoder_helper_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void encoder_helper_atomic_mode_set( + struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ } + +static void encoder_helper_disable(struct drm_encoder *encoder) +{ } + +static void encoder_helper_enable(struct drm_encoder *encoder) +{ } + +static int encoder_helper_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +static const struct drm_encoder_helper_funcs fbdevdrm_encoder_helper_funcs = { + .dpms = NULL, /* legacy */ + .mode_valid = encoder_helper_mode_valid, + .mode_fixup = encoder_helper_mode_fixup, + .prepare = NULL, /* legacy */ + .commit = NULL, /* legacy */ + .mode_set = NULL, /* legacy */ + .atomic_mode_set = encoder_helper_atomic_mode_set, + .get_crtc = NULL, /* legacy */ + .detect = NULL, /* legacy */ + .disable = encoder_helper_disable, + .enable = encoder_helper_enable, + .atomic_check = encoder_helper_atomic_check +}; + +static void encoder_destroy(struct drm_encoder *encoder) +{ } + +static const struct drm_encoder_funcs fbdevdrm_encoder_funcs = { + .reset = NULL, + .destroy = encoder_destroy, + .late_register = NULL, + .early_unregister = NULL, +}; + +/* + * Connector + */ + +static int connector_helper_get_modes(struct drm_connector *connector) +{ + return 0; +} + +static int connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + return connector_status_connected; +} + +static enum drm_mode_status connector_helper_mode_valid( + struct drm_connector *connector, struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static int connector_helper_atomic_check(struct drm_connector *connector, + struct drm_connector_state *state) +{ + return 0; +} + +static void connector_helper_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *state) +{ } + +static const struct drm_connector_helper_funcs + fbdevdrm_connector_helper_funcs = { + .get_modes = connector_helper_get_modes, + .detect_ctx = connector_helper_detect_ctx, + .mode_valid = connector_helper_mode_valid, + .best_encoder = NULL, /* use default */ + .atomic_best_encoder = NULL, /* use best_encoder instead */ + .atomic_check = connector_helper_atomic_check, + .atomic_commit = connector_helper_atomic_commit +}; + +static enum drm_connector_status connector_detect( + struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void connector_force(struct drm_connector *connector) +{ } + +static void connector_destroy(struct drm_connector *connector) +{ } + +static int connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val) +{ + return -EINVAL; +} + +static int connector_atomic_get_property( + struct drm_connector *connector, + const struct drm_connector_state *state, struct drm_property *property, + uint64_t *val) +{ + return -EINVAL; +} + +static const struct drm_connector_funcs fbdevdrm_connector_funcs = { + .dpms = NULL, /* not used by atomic drivers */ + .reset = drm_atomic_helper_connector_reset, + .detect = connector_detect, + .force = connector_force, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = NULL, + .late_register = NULL, + .early_unregister = NULL, + .destroy = connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = connector_atomic_set_property, + .atomic_get_property = connector_atomic_get_property, + .atomic_print_state = NULL +}; + +/* + * Mode config + */ + +static enum drm_mode_status mode_config_mode_valid( + struct drm_device *dev, const struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_mode_config_funcs fbdevdrm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .get_format_info = NULL, + /* DRM porting notes: the output_poll_changed callback is used by + * fb helpers to implement fbdev emulation. As fbdevdrm is built + * upon fbdev, this is basically the opposite. If you're porting + * an fbdev driver to DRM and enable fbdev emulation, this callback + * will become useful. + */ + .output_poll_changed = drm_fb_helper_output_poll_changed, + .mode_valid = mode_config_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .atomic_state_alloc = NULL, + .atomic_state_clear = NULL, + .atomic_state_free = NULL +}; + +/* + * Public interface + */ + +static int update_mode_config_from_fb_info(struct drm_mode_config* mode_config, struct fb_info *fb_info) +{ + /* DRM backporting notes: This function only exists to work around + * the fact that we don't know the hardware limitations. Here we + * test for the maximum supported resolution. If you're converting + * an fbdev driver to DRM, remove this function and simply fill in + * the actual hardware limits in the mode_config structure. + */ + + struct list_head *pos; + u32 xres = 0; + u32 yres = 0; + int num_modes = 0; + + list_for_each(pos, &fb_info->modelist) { + const struct fb_modelist *modelist = + container_of(pos, struct fb_modelist, list); + if (modelist->mode.xres > xres) + xres = modelist->mode.xres; + if (modelist->mode.yres > yres) + yres = modelist->mode.yres; + + ++num_modes; + } + + if (!xres || !yres) + return -ENODEV; + + mode_config->max_width = (int)xres; + mode_config->max_height = (int)yres; + mode_config->fb_base = fb_info->fix.smem_start; + + /* TODO: get preferred depth from screeninfo */ + mode_config->preferred_depth = 32; + + return 0; +} + +int fbdevdrm_modeset_init(struct fbdevdrm_modeset *modeset, + struct drm_device *dev, struct fb_info *fb_info) +{ + int ret; + + modeset->dev = dev; + modeset->fb_info = fb_info; + + drm_mode_config_init(dev); + ret = update_mode_config_from_fb_info(&dev->mode_config, fb_info); + if (ret) + goto err_drm_mode_config_cleanup; + dev->mode_config.funcs = &fbdevdrm_mode_config_funcs; + + /* One by one, we enable all stages of the display pipeline and + * connect them with each other. + */ + + ret = drm_crtc_init_with_planes(dev, &modeset->crtc, NULL, NULL, + &fbdevdrm_crtc_funcs, NULL); + if (ret) + goto err_drm_mode_config_cleanup; + drm_crtc_helper_add(&modeset->crtc, &fbdevdrm_crtc_helper_funcs); + + /* Use DRM_MODE_ENCODER_TYPE_DAC. It's true for many of the fbdev + * devices and doesn't imply hotplugging. */ + ret = drm_encoder_init(dev, &modeset->encoder, + &fbdevdrm_encoder_funcs, + DRM_MODE_ENCODER_DAC, NULL); + if (ret) + goto err_drm_mode_config_cleanup; + drm_encoder_helper_add(&modeset->encoder, + &fbdevdrm_encoder_helper_funcs); + + modeset->encoder.possible_crtcs = (1ul << modeset->crtc.index); + + ret = drm_connector_init(dev, &modeset->connector, + &fbdevdrm_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + if (ret) + goto err_drm_mode_config_cleanup; + drm_connector_helper_add(&modeset->connector, + &fbdevdrm_connector_helper_funcs); + + ret = drm_connector_register(&modeset->connector); + if (ret < 0) + goto err_drm_mode_config_cleanup; + + ret = drm_connector_attach_encoder(&modeset->connector, + &modeset->encoder); + if (ret) + goto err_drm_mode_config_cleanup; + + /* Final step: resetting the device's mode config creates + * state for all objects in the mode-setting pipeline. + */ + drm_mode_config_reset(dev); + + return 0; + +err_drm_mode_config_cleanup: + /* Also removes all CRTCs, encoders and connectors that we added. */ + drm_mode_config_cleanup(dev); + return ret; +} + +void fbdevdrm_modeset_cleanup(struct fbdevdrm_modeset *modeset) +{ + drm_mode_config_cleanup(modeset->dev); +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h new file mode 100644 index 000000000000..21e87caa8196 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_MODESET_H +#define FBDEVDRM_MODESET_H + +#include +#include +#include + +struct drm_device; +struct fb_info; + +struct fbdevdrm_modeset { + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; + + struct drm_device *dev; + struct fb_info *fb_info; +}; + +int fbdevdrm_modeset_init(struct fbdevdrm_modeset *modeset, + struct drm_device *dev, struct fb_info *fb_info); +void fbdevdrm_modeset_cleanup(struct fbdevdrm_modeset *modeset); + +#endif From patchwork Tue Mar 26 09:17:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870699 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B87D91669 for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A57532902E for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 99FFD2903E; Tue, 26 Mar 2019 09:17:59 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 90ED92902E for ; Tue, 26 Mar 2019 09:17:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727956AbfCZJR6 (ORCPT ); Tue, 26 Mar 2019 05:17:58 -0400 Received: from mx2.suse.de ([195.135.220.15]:39778 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726258AbfCZJR6 (ORCPT ); Tue, 26 Mar 2019 05:17:58 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id F012DAD65; Tue, 26 Mar 2019 09:17:55 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion Date: Tue, 26 Mar 2019 10:17:40 +0100 Message-Id: <20190326091744.11542-8-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/Makefile | 1 + drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c | 441 +++++++++++++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h | 26 ++ 3 files changed, 468 insertions(+) create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile index b8fab9d52faa..aef60d0f4888 100644 --- a/drivers/gpu/drm/fbdevdrm/Makefile +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -2,6 +2,7 @@ ccflags-y = -Iinclude/drm fbdevdrm-y := fbdevdrm_bo.o \ fbdevdrm_device.o \ fbdevdrm_drv.o \ + fbdevdrm_format.o \ fbdevdrm_modeset.o \ fbdevdrm_ttm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c new file mode 100644 index 000000000000..208f7c60e525 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_format.h" +#include +#include + +#if defined __BIG_ENDIAN +#define HOST_FUNC(_func) \ + _func ## _be +#elif defined __LITTLE_ENDIAN +#define HOST_FUNC(_func) \ + _func ## _le +#else +#error "Unsupported endianess" +#endif + +static bool is_c8(const struct fb_info* fb_info) +{ + return fb_info->var.bits_per_pixel == 8; +} + +static bool is_rgb565_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 16) && + (fb_info->var.red.offset == 0) && + (fb_info->var.red.length == 5) && + (fb_info->var.green.offset == 5) && + (fb_info->var.green.length == 6) && + (fb_info->var.blue.offset == 11) && + (fb_info->var.blue.length == 5); +} + +static bool is_bgr565_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 16) && + (fb_info->var.red.offset == 11) && + (fb_info->var.red.length == 5) && + (fb_info->var.green.offset == 5) && + (fb_info->var.green.length == 6) && + (fb_info->var.blue.offset == 0) && + (fb_info->var.blue.length == 5); +} + +static bool is_rgb888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 24) && + (fb_info->var.red.offset == 0) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 8) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 16) && + (fb_info->var.blue.length == 8); +} + +static bool is_bgr888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 24) && + (fb_info->var.red.offset == 16) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 8) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 0) && + (fb_info->var.blue.length == 8); +} + +static bool is_xrgb8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 8) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 16) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 24) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.length == 0); +} + +static bool is_xbgr8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 24) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 16) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 8) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.length == 0); +} + +static bool is_rgbx8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 0) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 8) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 16) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.length == 0); +} + +static bool is_bgrx8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 16) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 8) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 0) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.length == 0); +} + +static bool is_argb8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 8) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 16) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 24) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.offset == 0) && + (fb_info->var.transp.length == 8); +} + +static bool is_abgr8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 24) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 16) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 8) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.offset == 0) && + (fb_info->var.transp.length == 8); +} + +static bool is_rgba8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 0) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 8) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 16) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.offset == 24) && + (fb_info->var.transp.length == 8); +} + +static bool is_bgra8888_be(const struct fb_info* fb_info) +{ + return (fb_info->var.bits_per_pixel == 32) && + (fb_info->var.red.offset == 16) && + (fb_info->var.red.length == 8) && + (fb_info->var.green.offset == 8) && + (fb_info->var.green.length == 8) && + (fb_info->var.blue.offset == 0) && + (fb_info->var.blue.length == 8) && + (fb_info->var.transp.offset == 24) && + (fb_info->var.transp.length == 8); +} + +#define is_rgb565_le is_bgr565_be +#define is_bgr565_le is_rgb565_be +#define is_rgb888_le is_bgr888_be +#define is_bgr888_le is_rgb888_be +#define is_xrgb8888_le is_bgrx8888_be +#define is_xbgr8888_le is_rgbx8888_be +#define is_rgbx8888_le is_xbgr8888_be +#define is_bgrx8888_le is_xrgb8888_be +#define is_argb8888_le is_bgra8888_be +#define is_abgr8888_le is_rgba8888_be +#define is_rgba8888_le is_abgr8888_be +#define is_bgra8888_le is_argb8888_be + +struct format_map { + bool (*is_format)(const struct fb_info*); + uint32_t format; +}; + +uint32_t fbdevdrm_format_of_fb_info(const struct fb_info *fb_info) +{ + static const struct format_map map[] = { + { is_c8, DRM_FORMAT_C8 }, /* 256-color palette */ + { HOST_FUNC(is_rgb565), DRM_FORMAT_RGB565 }, + { HOST_FUNC(is_bgr565), DRM_FORMAT_BGR565 }, + { HOST_FUNC(is_rgb888), DRM_FORMAT_RGB888 }, + { HOST_FUNC(is_bgr888), DRM_FORMAT_BGR888 }, + { HOST_FUNC(is_xrgb8888), DRM_FORMAT_XRGB8888 }, + { HOST_FUNC(is_xbgr8888), DRM_FORMAT_XBGR8888 }, + { HOST_FUNC(is_rgbx8888), DRM_FORMAT_RGBX8888 }, + { HOST_FUNC(is_bgrx8888), DRM_FORMAT_BGRX8888 }, + { HOST_FUNC(is_argb8888), DRM_FORMAT_ARGB8888 }, + { HOST_FUNC(is_abgr8888), DRM_FORMAT_ABGR8888 }, + { HOST_FUNC(is_rgba8888), DRM_FORMAT_RGBA8888 }, + { HOST_FUNC(is_bgra8888), DRM_FORMAT_BGRA8888 } + }; + + size_t i; + + if (fb_info->fix.type != FB_TYPE_PACKED_PIXELS) + goto err; /* multi-plane formats are not supported */ + if (fb_info->var.bits_per_pixel < 8) + goto err; /* at least 8-bit color required */ + if (fb_info->var.grayscale == 1) + goto err; /* grayscale output is not supported */ + + for (i = 0; i < ARRAY_SIZE(map); ++i) { + if (map[i].is_format(fb_info)) + return map[i].format; + } + +err: + return DRM_FORMAT_INVALID; +} + +static void set_fb_bitfield(struct fb_bitfield *bits, __u32 offset, + __u32 length) +{ + bits->offset = offset; + bits->length = length; + bits->msb_right = 0; +} + +static void set_c8(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 8; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 0, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_rgb565_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 16; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 5); + set_fb_bitfield(&fb_var->green, 5, 6); + set_fb_bitfield(&fb_var->blue, 11, 5); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_bgr565_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 16; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 11, 5); + set_fb_bitfield(&fb_var->green, 5, 6); + set_fb_bitfield(&fb_var->blue, 0, 5); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_rgb888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 24; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 16, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_bgr888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 24; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 16, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_xrgb8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 8, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 24, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_xbgr8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 24, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 8, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_rgbx8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 16, 8); + set_fb_bitfield(&fb_var->transp, 24, 0); + fb_var->nonstd = 0; +} + +static void set_bgrx8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 16, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 24, 0); + fb_var->nonstd = 0; +} + +static void set_argb8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 8, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 24, 8); + set_fb_bitfield(&fb_var->transp, 0, 8); + fb_var->nonstd = 0; +} + +static void set_abgr8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 24, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 8, 8); + set_fb_bitfield(&fb_var->transp, 0, 8); + fb_var->nonstd = 0; +} + +static void set_rgba8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 16, 8); + set_fb_bitfield(&fb_var->transp, 24, 8); + fb_var->nonstd = 0; +} + +static void set_bgra8888_be(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 16, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 24, 8); + fb_var->nonstd = 0; +} + +#define set_rgb565_le set_bgr565_be +#define set_bgr565_le set_rgb565_be +#define set_rgb888_le set_bgr888_be +#define set_bgr888_le set_rgb888_be +#define set_xrgb8888_le set_bgrx8888_be +#define set_xbgr8888_le set_rgbx8888_be +#define set_rgbx8888_le set_xbgr8888_be +#define set_bgrx8888_le set_xrgb8888_be +#define set_argb8888_le set_bgra8888_be +#define set_abgr8888_le set_rgba8888_be +#define set_rgba8888_le set_abgr8888_be +#define set_bgra8888_le set_argb8888_be + +int fbdevdrm_update_fb_var_screeninfo_from_format( + struct fb_var_screeninfo *fb_var, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_C8: + set_c8(fb_var); + break; + case DRM_FORMAT_RGB565: + HOST_FUNC(set_rgb565)(fb_var); + break; + case DRM_FORMAT_BGR565: + HOST_FUNC(set_bgr565)(fb_var); + break; + case DRM_FORMAT_RGB888: + HOST_FUNC(set_rgb888)(fb_var); + break; + case DRM_FORMAT_BGR888: + HOST_FUNC(set_bgr888)(fb_var); + break; + case DRM_FORMAT_XRGB8888: + HOST_FUNC(set_xrgb8888)(fb_var); + break; + case DRM_FORMAT_XBGR8888: + HOST_FUNC(set_xbgr8888)(fb_var); + break; + case DRM_FORMAT_RGBX8888: + HOST_FUNC(set_rgbx8888)(fb_var); + break; + case DRM_FORMAT_BGRX8888: + HOST_FUNC(set_bgrx8888)(fb_var); + break; + case DRM_FORMAT_ARGB8888: + HOST_FUNC(set_argb8888)(fb_var); + break; + case DRM_FORMAT_ABGR8888: + HOST_FUNC(set_abgr8888)(fb_var); + break; + case DRM_FORMAT_RGBA8888: + HOST_FUNC(set_rgba8888)(fb_var); + break; + case DRM_FORMAT_BGRA8888: + HOST_FUNC(set_bgra8888)(fb_var); + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h new file mode 100644 index 000000000000..e837978ac8fc --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_FORMAT_H +#define FBDEVDRM_FORMAT_H + +#include + +struct fb_info; +struct fb_var_screeninfo; + +uint32_t fbdevdrm_format_of_fb_info(const struct fb_info *fb_info); + +int fbdevdrm_update_fb_var_screeninfo_from_format( + struct fb_var_screeninfo *fb_var, uint32_t format); + +#endif From patchwork Tue Mar 26 09:17:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870703 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BAB861669 for ; Tue, 26 Mar 2019 09:18:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A60D629036 for ; Tue, 26 Mar 2019 09:18:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 99EDF2903E; Tue, 26 Mar 2019 09:18:00 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DB50429036 for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729760AbfCZJR7 (ORCPT ); Tue, 26 Mar 2019 05:17:59 -0400 Received: from mx2.suse.de ([195.135.220.15]:39792 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1730594AbfCZJR7 (ORCPT ); Tue, 26 Mar 2019 05:17:59 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 35850AE1D; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 08/11] drm/fbdevdrm: Add mode conversion DRM <-> fbdev Date: Tue, 26 Mar 2019 10:17:41 +0100 Message-Id: <20190326091744.11542-9-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/Makefile | 1 + drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c | 153 ++++++++++++++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h | 46 +++++++ 3 files changed, 200 insertions(+) create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile index aef60d0f4888..2ca906a3258b 100644 --- a/drivers/gpu/drm/fbdevdrm/Makefile +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -3,6 +3,7 @@ fbdevdrm-y := fbdevdrm_bo.o \ fbdevdrm_device.o \ fbdevdrm_drv.o \ fbdevdrm_format.o \ + fbdevdrm_modes.o \ fbdevdrm_modeset.o \ fbdevdrm_ttm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c new file mode 100644 index 000000000000..bd3ad691e7ce --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_modes.h" +#include +#include + +void drm_mode_update_from_fb_videomode(struct drm_display_mode *mode, + const struct fb_videomode *fb_mode) +{ + mode->type = DRM_MODE_TYPE_DRIVER; + + mode->clock = PICOS2KHZ(fb_mode->pixclock); + + mode->hdisplay = fb_mode->xres; + mode->hsync_start = mode->hdisplay + fb_mode->right_margin; + mode->hsync_end = mode->hsync_start + fb_mode->hsync_len; + mode->htotal = mode->hsync_end + fb_mode->left_margin; + mode->hskew = 0; + + mode->vdisplay = fb_mode->yres; + mode->vsync_start = mode->vdisplay + fb_mode->lower_margin; + mode->vsync_end = mode->vsync_start + fb_mode->vsync_len; + mode->vtotal = mode->vsync_end + fb_mode->upper_margin; + mode->vscan = 0; + + mode->flags = 0; + + if (fb_mode->sync & FB_SYNC_HOR_HIGH_ACT) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else + mode->flags |= DRM_MODE_FLAG_NHSYNC; + + if (fb_mode->sync & FB_SYNC_VERT_HIGH_ACT) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else + mode->flags |= DRM_MODE_FLAG_NVSYNC; + + if (fb_mode->sync & FB_SYNC_COMP_HIGH_ACT) + mode->flags |= DRM_MODE_FLAG_CSYNC | DRM_MODE_FLAG_PCSYNC; + + if (fb_mode->vmode & FB_VMODE_INTERLACED) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + + if (fb_mode->vmode & FB_VMODE_DOUBLE) + mode->flags |= DRM_MODE_FLAG_DBLSCAN; + + mode->width_mm = 0; + mode->height_mm = 0; + + mode->vrefresh = fb_mode->refresh; + mode->hsync = mode->clock / mode->vtotal; + + /* final step; depends on previous setup */ + if (fb_mode->name) { + strncpy(mode->name, fb_mode->name, sizeof(mode->name) - 1); + mode->name[sizeof(mode->name) - 1] = '\0'; + } else { + drm_mode_set_name(mode); + } +} + +void drm_mode_update_from_fb_var_screeninfo( + struct drm_display_mode *mode, const struct fb_var_screeninfo *fb_var) +{ + struct fb_videomode fb_mode; + + fb_var_to_videomode(&fb_mode, fb_var); + drm_mode_update_from_fb_videomode(mode, &fb_mode); +} + +struct drm_display_mode* drm_mode_create_from_fb_videomode( + struct drm_device *dev, const struct fb_videomode *fb_mode) +{ + /* cleared to '0' */ + struct drm_display_mode *mode = drm_mode_create(dev); + if (!mode) + return NULL; + + drm_mode_update_from_fb_videomode(mode, fb_mode); + + return mode; +} + +void +fbdevdrm_update_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode) +{ + fb_mode->name = NULL; + fb_mode->refresh = mode->vrefresh; + fb_mode->xres = mode->hdisplay; + fb_mode->yres = mode->vdisplay; + fb_mode->pixclock = KHZ2PICOS(mode->clock); + fb_mode->left_margin = mode->htotal - mode->hsync_end; + fb_mode->right_margin = mode->hsync_start - mode->hdisplay; + fb_mode->upper_margin = mode->vtotal - mode->vsync_end; + fb_mode->lower_margin = mode->vsync_start - mode->vdisplay; + fb_mode->hsync_len = mode->hsync_end - mode->hsync_start; + fb_mode->vsync_len = mode->vsync_end - mode->vsync_start; + + fb_mode->sync = 0; + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + fb_mode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + fb_mode->sync |= FB_SYNC_VERT_HIGH_ACT; + if (mode->flags & (DRM_MODE_FLAG_CSYNC | DRM_MODE_FLAG_PCSYNC)) + fb_mode->sync |= FB_SYNC_COMP_HIGH_ACT; + + fb_mode->vmode = 0; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + fb_mode->vmode |= FB_VMODE_INTERLACED; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + fb_mode->vmode |= FB_VMODE_DOUBLE; + + fb_mode->flag = 0; +} + +void +fbdevdrm_init_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode) +{ + memset(fb_mode, 0, sizeof(*fb_mode)); + fbdevdrm_update_fb_videomode_from_mode(fb_mode, mode); +} + +void +fbdevdrm_update_fb_var_screeninfo_from_mode(struct fb_var_screeninfo *fb_var, + const struct drm_display_mode *mode) +{ + struct fb_videomode fb_mode; + fbdevdrm_init_fb_videomode_from_mode(&fb_mode, mode); + fb_videomode_to_var(fb_var, &fb_mode); + + fb_var->height = mode->height_mm; + fb_var->width = mode->width_mm; +} + +void +fbdevdrm_init_fb_var_screeninfo_from_mode(struct fb_var_screeninfo *fb_var, + const struct drm_display_mode *mode) +{ + memset(fb_var, 0, sizeof(*fb_var)); + fbdevdrm_update_fb_var_screeninfo_from_mode(fb_var, mode); +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h new file mode 100644 index 000000000000..f88a86a83858 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_MODES_H +#define FBDEVDRM_MODES_H + +struct drm_device; +struct drm_display_mode; +struct fb_videomode; +struct fb_var_screeninfo; + +void drm_mode_update_from_fb_videomode(struct drm_display_mode *mode, + const struct fb_videomode *fb_mode); + +void drm_mode_update_from_fb_var_screeninfo( + struct drm_display_mode *mode, const struct fb_var_screeninfo *fb_var); + +struct drm_display_mode* drm_mode_create_from_fb_videomode( + struct drm_device *dev, const struct fb_videomode *fb_mode); + +void +fbdevdrm_update_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode); + +void +fbdevdrm_init_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode); + +void +fbdevdrm_update_fb_var_screeninfo_from_mode(struct fb_var_screeninfo *var, + const struct drm_display_mode *mode); + +void +fbdevdrm_init_fb_var_screeninfo_from_mode(struct fb_var_screeninfo *var, + const struct drm_display_mode *mode); + +#endif From patchwork Tue Mar 26 09:17:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870709 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 240A215AC for ; Tue, 26 Mar 2019 09:18:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 10BB62902D for ; Tue, 26 Mar 2019 09:18:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0575B29036; Tue, 26 Mar 2019 09:18:02 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C61A629035 for ; Tue, 26 Mar 2019 09:17:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726258AbfCZJR6 (ORCPT ); Tue, 26 Mar 2019 05:17:58 -0400 Received: from mx2.suse.de ([195.135.220.15]:39724 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729760AbfCZJR6 (ORCPT ); Tue, 26 Mar 2019 05:17:58 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 71E83AD95; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 09/11] drm/fbdevdrm: Add primary plane Date: Tue, 26 Mar 2019 10:17:42 +0100 Message-Id: <20190326091744.11542-10-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/Makefile | 1 + drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c | 42 ++ drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h | 7 + drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 9 +- drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h | 2 + drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 ++++++++++++++++++++ drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h | 27 ++ 7 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile index 2ca906a3258b..5507152d8187 100644 --- a/drivers/gpu/drm/fbdevdrm/Makefile +++ b/drivers/gpu/drm/fbdevdrm/Makefile @@ -5,6 +5,7 @@ fbdevdrm-y := fbdevdrm_bo.o \ fbdevdrm_format.o \ fbdevdrm_modes.o \ fbdevdrm_modeset.o \ + fbdevdrm_primary.o \ fbdevdrm_ttm.o obj-$(CONFIG_DRM_FBDEVDRM) += fbdevdrm.o diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c index bd3ad691e7ce..8dea7ef369dc 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c @@ -11,8 +11,12 @@ */ #include "fbdevdrm_modes.h" +#include +#include /* for TASK_COMM_LEN in */ +#include #include #include +#include "fbdevdrm_format.h" void drm_mode_update_from_fb_videomode(struct drm_display_mode *mode, const struct fb_videomode *fb_mode) @@ -151,3 +155,41 @@ fbdevdrm_init_fb_var_screeninfo_from_mode(struct fb_var_screeninfo *fb_var, memset(fb_var, 0, sizeof(*fb_var)); fbdevdrm_update_fb_var_screeninfo_from_mode(fb_var, mode); } + +int fbdevdrm_update_fb_var_screeninfo_from_framebuffer( + struct fb_var_screeninfo *fb_var, struct drm_framebuffer *fb, + size_t vram_size) +{ + unsigned int width, pitch; + uint64_t cpp, lines; + int ret; + + /* Our virtual screen covers all the graphics memory (sans some + * trailing bytes). This allows for setting the scanout buffer's + * address with fb_pan_display(). + */ + + width = fb->pitches[0]; + cpp = drm_format_plane_cpp(fb->format[0].format, 0); + do_div(width, cpp); + + if (width > (__u32)-1) + return -EINVAL; /* would overflow fb_var->xres_virtual */ + + pitch = fb->pitches[0]; + lines = vram_size; + do_div(lines, pitch); + + if (lines > (__u32)-1) + return -EINVAL; /* would overflow fb_var->yres_virtual */ + + fb_var->xres_virtual = width; + fb_var->yres_virtual = lines; + + ret = fbdevdrm_update_fb_var_screeninfo_from_format( + fb_var, fb->format[0].format); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h index f88a86a83858..925eea78e3f0 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h @@ -13,8 +13,11 @@ #ifndef FBDEVDRM_MODES_H #define FBDEVDRM_MODES_H +#include + struct drm_device; struct drm_display_mode; +struct drm_framebuffer; struct fb_videomode; struct fb_var_screeninfo; @@ -43,4 +46,8 @@ void fbdevdrm_init_fb_var_screeninfo_from_mode(struct fb_var_screeninfo *var, const struct drm_display_mode *mode); +int fbdevdrm_update_fb_var_screeninfo_from_framebuffer( + struct fb_var_screeninfo *fb_var, struct drm_framebuffer *fb, + size_t vram_size); + #endif diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c index 585f3478f190..3473b85acbf1 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c @@ -20,6 +20,7 @@ #include #include #include +#include "fbdevdrm_primary.h" /* * CRTC @@ -376,7 +377,13 @@ int fbdevdrm_modeset_init(struct fbdevdrm_modeset *modeset, * connect them with each other. */ - ret = drm_crtc_init_with_planes(dev, &modeset->crtc, NULL, NULL, + ret = fbdevdrm_init_primary_plane_from_fb_info( + &modeset->primary_plane, dev, 0, fb_info); + if (ret) + goto err_drm_mode_config_cleanup; + + ret = drm_crtc_init_with_planes(dev, &modeset->crtc, + &modeset->primary_plane, NULL, &fbdevdrm_crtc_funcs, NULL); if (ret) goto err_drm_mode_config_cleanup; diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h index 21e87caa8196..ec753014aba1 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h @@ -16,11 +16,13 @@ #include #include #include +#include struct drm_device; struct fb_info; struct fbdevdrm_modeset { + struct drm_plane primary_plane; struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector connector; diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c new file mode 100644 index 000000000000..8ba8e6bd1c14 --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c @@ -0,0 +1,498 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#include "fbdevdrm_primary.h" +#include +#include +#include +#include +#include +#include "fbdevdrm_bo.h" +#include "fbdevdrm_format.h" +#include "fbdevdrm_modes.h" +#include "fbdevdrm_modeset.h" + +static struct fbdevdrm_modeset* fbdevdrm_modeset_of_primary_plane( + struct drm_plane *primary_plane) +{ + return container_of(primary_plane, struct fbdevdrm_modeset, + primary_plane); +} + +/* + * Primary plane + */ + +static int primary_plane_helper_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_gem_object *gem; + struct fbdevdrm_bo *fbo; + int ret; + + if (!new_state->fb) + return 0; + + gem = new_state->fb->obj[0]; + fbo = fbdevdrm_bo_of_gem(gem); + + ret = fbdevdrm_bo_pin(fbo, TTM_PL_FLAG_VRAM); + if (ret) + return ret; + + return 0; +} + +static void primary_plane_helper_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_gem_object *gem; + struct fbdevdrm_bo *fbo; + + if (!old_state->fb) + return; + + gem = old_state->fb->obj[0]; + fbo = fbdevdrm_bo_of_gem(gem); + + fbdevdrm_bo_unpin(fbo); +} + +static void fbdevdrm_update_fb_var_screeninfo_from_crtc_state( + struct fb_var_screeninfo *fb_var, struct drm_crtc_state* crtc_state) +{ + fbdevdrm_update_fb_var_screeninfo_from_mode(fb_var, &crtc_state->adjusted_mode); +} + +static int primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *new_crtc_state; + int ret; + struct fbdevdrm_modeset *modeset; + struct fb_var_screeninfo fb_var; + + if (!state->crtc) + return 0; + + new_crtc_state = drm_atomic_get_new_crtc_state(state->state, + state->crtc); + if (!new_crtc_state) + return 0; + + ret = drm_atomic_helper_check_plane_state(state, new_crtc_state, + 1 << 16, 1 << 16, + false, true); + if (ret < 0) { + DRM_ERROR("fbdrmdev: %s:%d ret=%d:\n", __func__, __LINE__, ret); + return ret; + } + + if (!state->visible || !state->fb) + return 0; + + /* Virtual screen sizes are not supported. + */ + + if (drm_rect_width(&state->dst) != state->fb->width || + drm_rect_height(&state->dst) != state->fb->height) { + DRM_ERROR("fbdevdrm: %s:%d: virtual screen sizes not supported\n", __func__, __LINE__); + return -EINVAL; + } + if (state->dst.x1 || state->dst.y1) { + DRM_ERROR("fbdevdrm: %s:%d: virtual screen offset not supported\n", __func__, __LINE__); + return -EINVAL; + } + + /* Pixel formats have to be compatible with fbdev. This is + * usually some variation of XRGB. + */ + + if (!plane->state || + !plane->state->fb || + plane->state->fb->format[0].format != state->fb->format[0].format) { + + modeset = fbdevdrm_modeset_of_primary_plane(plane); + + if (modeset->fb_info->fbops->fb_check_var) { + memcpy(&fb_var, &modeset->fb_info->var, + sizeof(fb_var)); + fbdevdrm_update_fb_var_screeninfo_from_crtc_state( + &fb_var, new_crtc_state); + fbdevdrm_update_fb_var_screeninfo_from_framebuffer( + &fb_var, state->fb, + modeset->fb_info->fix.smem_len); + ret = modeset->fb_info->fbops->fb_check_var( + &fb_var, modeset->fb_info); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int set_palette_cmap(struct fb_info* fb_info) +{ + __u32 len; + const struct fb_cmap* default_cmap; + struct fb_cmap cmap; + int ret; + const __u32 gamma_len[3] = { + fb_info->var.red.length, + fb_info->var.green.length, + fb_info->var.blue.length + }; + + len = max3(gamma_len[0], gamma_len[1], gamma_len[2]); + if (!len || (len > 31)) { + DRM_ERROR("fbdevdrm: gamma LUT has invalid bit count" + " of %u\n", (unsigned int)len); + return -EINVAL; + } + + default_cmap = fb_default_cmap(1ul << len); + if (!default_cmap) + return -EINVAL; + + memset(&cmap, 0, sizeof(cmap)); + ret = fb_alloc_cmap(&cmap, default_cmap->len, 0); + if (ret) + return ret; + ret = fb_copy_cmap(default_cmap, &cmap); + if (ret) + goto err_fb_dealloc_cmap; + ret = fb_set_cmap(&cmap, fb_info); + if (ret) + return ret; + fb_dealloc_cmap(&cmap); + + return 0; + +err_fb_dealloc_cmap: + fb_dealloc_cmap(&cmap); + return ret; +} + +static int set_linear_cmap(struct fb_info* fb_info) +{ + struct fb_cmap cmap; + int ret; + size_t i; + unsigned int j; + u16 *lut; + u16 incr; + u16 *gamma_lut[3]; + __u32 len; + const __u32 gamma_len[3] = { + fb_info->var.red.length, + fb_info->var.green.length, + fb_info->var.blue.length + }; + + len = max3(gamma_len[0], gamma_len[1], gamma_len[2]); + if (!len || (len > 8)) { + DRM_ERROR("fbdevdrm: gamma LUT has invalid bit count" + " of %u\n", (unsigned int)len); + return -EINVAL; + } + + memset(&cmap, 0, sizeof(cmap)); + ret = fb_alloc_cmap(&cmap, 1ul << len, 0); + if (ret) + return ret; + + gamma_lut[0] = cmap.red; + gamma_lut[1] = cmap.green; + gamma_lut[2] = cmap.blue; + + for (i = 0; i < ARRAY_SIZE(gamma_lut); ++i) { + lut = gamma_lut[i]; + len = 1ul << gamma_len[i]; + incr = 0x10000u >> gamma_len[i]; + for (j = 0; j < len; ++j, ++lut) { + *lut = incr * j; + } + /* In order to have no intensity at index 0 and full + * intensity at the final index of the LUT, we fix-up the + * table's final entries. The fix-up makes intensity grow + * faster near the final entries of the gamma LUT. The human + * eye is more sensitive to changes to the lower intensities, + * so this is probably not directly perceivable. + */ + for (lut -= gamma_len[i], j = gamma_len[i]; j > 0; ++lut) { + --j; + *lut += (incr >> j) - 1; /* subtract 1 to not + * overflow the LUT's + * final entry */ + } + } + + ret = fb_set_cmap(&cmap, fb_info); + if (ret) + goto err_fb_dealloc_cmap; + fb_dealloc_cmap(&cmap); + + return 0; + +err_fb_dealloc_cmap: + fb_dealloc_cmap(&cmap); + return -EINVAL; +} + +static int set_cmap(struct fb_info *fb_info) +{ + int ret = 0; + + switch (fb_info->fix.visual) { + case FB_VISUAL_PSEUDOCOLOR: + ret = set_palette_cmap(fb_info); + break; + case FB_VISUAL_DIRECTCOLOR: + ret = set_linear_cmap(fb_info); + break; + default: + break; + } + + return ret; +} + +static void primary_plane_helper_atomic_update( + struct drm_plane *plane, struct drm_plane_state *old_state) +{ + struct fbdevdrm_modeset *modeset; + uint32_t format; + struct fb_var_screeninfo fb_var; + int ret; + struct drm_gem_object *gem; + struct fbdevdrm_bo *fbo; + __u32 line_length; + uint64_t yoffset; + uint32_t xoffset; + + modeset = fbdevdrm_modeset_of_primary_plane(plane); + + format = fbdevdrm_format_of_fb_info(modeset->fb_info); + + /* DRM porting notes: Some fbdev drivers report alpha channels for + * their framebuffer, even though they don't support transparent + * primary planes. For the format test below, we ignore the alpha + * channel and use the non-transparent equivalent of the pixel format. + * If you're porting an fbdev driver to DRM, remove this switch + * statement and report the correct format instead. + */ + switch (format) { + case DRM_FORMAT_ARGB8888: + format = DRM_FORMAT_XRGB8888; + break; + case DRM_FORMAT_ABGR8888: + format = DRM_FORMAT_XBGR8888; + break; + case DRM_FORMAT_RGBA8888: + format = DRM_FORMAT_RGBX8888; + break; + case DRM_FORMAT_BGRA8888: + format = DRM_FORMAT_BGRX8888; + break; + default: + break; + } + + if ((format != plane->state->fb->format[0].format) || + (modeset->fb_info->var.xres_virtual != plane->state->fb->width)) { + + /* Pixel format changed, update fb_info accordingly + */ + + memcpy(&fb_var, &modeset->fb_info->var, sizeof(fb_var)); + ret = fbdevdrm_update_fb_var_screeninfo_from_framebuffer( + &fb_var, plane->state->fb, + modeset->fb_info->fix.smem_len); + if (ret) + return; + + fb_var.activate = FB_ACTIVATE_NOW; + + ret = fb_set_var(modeset->fb_info, &fb_var); + if (ret) { + DRM_ERROR("fbdevdrm: fb_set_var() failed: %d\n", ret); + return; + } + } + + if (!old_state->fb || /* first-time update */ + (format != plane->state->fb->format[0].format)) { + + /* DRM porting notes: Below we set the LUTs for palette and + * gamma correction. This is required by some fbdev drivers, + * such as nvidiafb and atyfb, which don't initialize the + * table to pass-through the framebuffer values unchanged. This + * is actually CRTC state, but the respective function + * crtc_helper_mode_set_nofb() is only called when a CRTC + * property changes, changes in color formats are not handled + * there. When you're porting a fbdev driver to DRM, remove + * the call. Gamma LUTs are CRTC properties and should be + * handled there. Either remove gamma correction or set up + * the respective CRTC properties for userspace. + */ + set_cmap(modeset->fb_info); + } + + /* With the fb interface, we cannot directly program + * the scanout buffer's address. Instead we use the + * panning function to point the graphics card to the + * buffer's location. + */ + + gem = plane->state->fb->obj[0]; + fbo = fbdevdrm_bo_of_gem(gem); + + line_length = plane->state->fb->pitches[0]; + yoffset = fbo->bo.offset; + xoffset = do_div(yoffset, line_length); + if (yoffset > (__u32)-1) { + /* The value of yoffset doesn't fit into a 32-bit value, + * so we cannot use it for display panning. Either the + * graphics card has GiBs of VRAM or this is a bug with + * memory management. */ + DRM_ERROR("fbdevdrm: buffer object is not aligned to a " + "multiple of the scanline size.\n"); + return; + } else if (xoffset) { + /* The buffer starts in the middle of a scanline. The + * memory manager should have prevented this. This + * problem indicates a bug with the buffer aligning. */ + DRM_ERROR("fbdevdrm: buffer object is not aligned to a " + "multiple of the scanline size.\n"); + return; + } + + memcpy(&fb_var, &modeset->fb_info->var, sizeof(fb_var)); + fb_var.xoffset = xoffset; + fb_var.yoffset = yoffset; + + ret = fb_pan_display(modeset->fb_info, &fb_var); + if (ret) { + DRM_ERROR("fbdevdrm: fb_pan_display() failed: %d\n", ret); + return; + } +} + +static void primary_plane_helper_atomic_disable( + struct drm_plane *plane, struct drm_plane_state *old_state) +{ } + +static int primary_plane_helper_atomic_async_check( + struct drm_plane *plane, struct drm_plane_state *state) +{ + return 0; +} + +static void primary_plane_helper_atomic_async_update( + struct drm_plane *plane, struct drm_plane_state *new_state) +{ + drm_plane_cleanup(plane); +} + +static const struct drm_plane_helper_funcs primary_plane_helper_funcs = { + .prepare_fb = primary_plane_helper_prepare_fb, + .cleanup_fb = primary_plane_helper_cleanup_fb, + .atomic_check = primary_plane_helper_atomic_check, + .atomic_update = primary_plane_helper_atomic_update, + .atomic_disable = primary_plane_helper_atomic_disable, + .atomic_async_check = primary_plane_helper_atomic_async_check, + .atomic_async_update = primary_plane_helper_atomic_async_update +}; + +static void primary_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); +} + +static const struct drm_plane_funcs primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = primary_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .set_property = NULL, /* unused */ + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_set_property = NULL, /* unused */ + .atomic_get_property = NULL, /* unused */ + .late_register = NULL, /* unused */ + .early_unregister = NULL, /* unused */ + .atomic_print_state = NULL, /* unused */ + .format_mod_supported = NULL /* unused */ +}; + +static const uint32_t* +formats_from_fb_info(const struct fb_info* fb_info, unsigned int* format_count) +{ + /* TODO: Detect the actually supported formats or have some + * sort of whitelist for known hardware devices. + */ + static const uint32_t formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565 + }; + + *format_count = ARRAY_SIZE(formats); + + return formats; +} + +int fbdevdrm_init_primary_plane_from_fb_info(struct drm_plane *plane, + struct drm_device *dev, + uint32_t possible_crtcs, + struct fb_info *fb_info) +{ + uint32_t cur_format; + const uint32_t* format; + unsigned int format_count; + int ret; + + /* We first try to find the supported pixel formats from the + * fb_info's hardware settings. If that fails, we take the + * current settings. */ + format = formats_from_fb_info(fb_info, &format_count); + if (!format_count) { + cur_format = fbdevdrm_format_of_fb_info(fb_info); + format = &cur_format; + format_count = 1; + } + if (!format_count) + return -ENODEV; + + ret = drm_universal_plane_init(dev, plane, possible_crtcs, + &primary_plane_funcs, + format, format_count, + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret < 0) + return ret; + drm_plane_helper_add(plane, &primary_plane_helper_funcs); + + ret = drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0); + if (ret < 0) + goto err_drm_plane_cleanup; + + ret = drm_plane_create_zpos_immutable_property(plane, 0); + if (ret < 0) + goto err_drm_plane_cleanup; + + return 0; + +err_drm_plane_cleanup: + drm_plane_cleanup(plane); + return ret; +} diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h new file mode 100644 index 000000000000..529c272c6e0b --- /dev/null +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * One purpose of this driver is to allow for easy conversion of framebuffer + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to + * relicense this file under the terms of a license of your choice if you're + * porting a framebuffer driver. In order to do so, update the SPDX license + * identifier to the new license and remove this exception. + * + * If you add code to this file, please ensure that it's compatible with the + * stated exception. + */ + +#ifndef FBDEVDRM_PRIMARY_H +#define FBDEVDRM_PRIMARY_H + +#include + +struct drm_device; +struct drm_plane; +struct fb_info; + +int fbdevdrm_init_primary_plane_from_fb_info(struct drm_plane *plane, + struct drm_device *dev, + uint32_t possible_crtcs, + struct fb_info *fb_info); + +#endif From patchwork Tue Mar 26 09:17:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EAB17186E for ; Tue, 26 Mar 2019 09:18:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D7DAC2873C for ; Tue, 26 Mar 2019 09:18:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CBC5929036; Tue, 26 Mar 2019 09:18:00 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5211C2902D for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726270AbfCZJR7 (ORCPT ); Tue, 26 Mar 2019 05:17:59 -0400 Received: from mx2.suse.de ([195.135.220.15]:39820 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729334AbfCZJR6 (ORCPT ); Tue, 26 Mar 2019 05:17:58 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id AEFF7AE27; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 10/11] drm/fbdevdrm: Add CRTC Date: Tue, 26 Mar 2019 10:17:43 +0100 Message-Id: <20190326091744.11542-11-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 150 +++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c index 3473b85acbf1..87f56ec76edf 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c @@ -18,10 +18,18 @@ #include #include #include +#include #include #include +#include "fbdevdrm_modes.h" #include "fbdevdrm_primary.h" +static struct fbdevdrm_modeset* fbdevdrm_modeset_of_crtc( + struct drm_crtc *crtc) +{ + return container_of(crtc, struct fbdevdrm_modeset, crtc); +} + /* * CRTC */ @@ -29,18 +37,124 @@ static enum drm_mode_status crtc_helper_mode_valid( struct drm_crtc *crtc, const struct drm_display_mode *mode) { + static const unsigned char bits_per_pixel[] = { + 32, 16, 8, 24, 15 + }; + + struct fbdevdrm_modeset *modeset; + struct fb_var_screeninfo fb_var; + size_t i; + int ret; + + modeset = fbdevdrm_modeset_of_crtc(crtc); + + if (!modeset->fb_info->fbops->fb_check_var) + return MODE_OK; + + for (i = 0; i < ARRAY_SIZE(bits_per_pixel); ++i) { + + memcpy(&fb_var, &modeset->fb_info->var, sizeof(fb_var)); + fbdevdrm_update_fb_var_screeninfo_from_mode(&fb_var, mode); + fb_var.bits_per_pixel = bits_per_pixel[i]; + + ret = modeset->fb_info->fbops->fb_check_var(&fb_var, modeset->fb_info); + if (ret) + continue; /* generally not supported */ + if ((mode->hdisplay != fb_var.xres) || + (mode->vdisplay != fb_var.yres)) + continue; /* unsupported resolution */ + if (KHZ2PICOS(mode->clock) != fb_var.pixclock) + continue; /* unsupported pixel clock */ + + break; /* mode successfully tested */ + } + if (i == ARRAY_SIZE(bits_per_pixel)) + return MODE_BAD; /* mode is not support */ + return MODE_OK; } +static int fbdevdrm_update_fb_var_screeninfo_from_crtc( + struct fb_var_screeninfo *fb_var, struct drm_crtc* crtc) +{ + struct drm_plane *primary = crtc->primary; + struct fbdevdrm_modeset *modeset = fbdevdrm_modeset_of_crtc(crtc); + + if (primary && primary->state && primary->state->fb) + return fbdevdrm_update_fb_var_screeninfo_from_framebuffer( + fb_var, primary->state->fb, + modeset->fb_info->fix.smem_len); + + fb_var->xres_virtual = fb_var->xres; + fb_var->yres_virtual = fb_var->yres; + fb_var->bits_per_pixel = modeset->dev->mode_config.preferred_depth; + + return 0; +} + static bool crtc_helper_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct fbdevdrm_modeset *modeset; + struct fb_var_screeninfo fb_var; + int ret; + + modeset = fbdevdrm_modeset_of_crtc(crtc); + + if (!modeset->fb_info->fbops->fb_check_var) + return true; + + fbdevdrm_init_fb_var_screeninfo_from_mode(&fb_var, mode); + + ret = fbdevdrm_update_fb_var_screeninfo_from_crtc(&fb_var, crtc); + if (ret) + return true; + + ret = modeset->fb_info->fbops->fb_check_var(&fb_var, modeset->fb_info); + if (ret < 0) + return false; + + drm_mode_update_from_fb_var_screeninfo(adjusted_mode, &fb_var); + return true; } static void crtc_helper_mode_set_nofb(struct drm_crtc *crtc) -{ } +{ + struct fbdevdrm_modeset *modeset; + struct fb_var_screeninfo fb_var; + int ret; + + /* As this is atomic mode setting, any function call is not + * allowed to fail. If it does, an additional test should be + * added to crtc_helper_atomic_check(). + */ + + modeset = fbdevdrm_modeset_of_crtc(crtc); + + memset(&fb_var, 0, sizeof(fb_var)); + fbdevdrm_update_fb_var_screeninfo_from_mode(&fb_var, &crtc->state->adjusted_mode); + + if (crtc->primary && crtc->primary->state && crtc->primary->state->fb) { + ret = fbdevdrm_update_fb_var_screeninfo_from_framebuffer( + &fb_var, crtc->primary->state->fb, + modeset->fb_info->fix.smem_len); + if (ret) + return; + } else { + fb_var.xres_virtual = fb_var.xres; + fb_var.yres_virtual = fb_var.yres; + } + + fb_var.activate = FB_ACTIVATE_NOW; + + ret = fb_set_var(modeset->fb_info, &fb_var); + if (ret) { + DRM_ERROR("fbdevdrm: fb_set_var() failed: %d\n", ret); + return; + } +} static int crtc_helper_mode_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -53,6 +167,40 @@ static int crtc_helper_mode_set_base_atomic(struct drm_crtc *crtc, static int crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { + struct fbdevdrm_modeset *modeset; + struct fb_videomode fb_mode, fb_var_mode; + + modeset = fbdevdrm_modeset_of_crtc(crtc); + + /* DRM porting notes: when fbcon takes over the console, it regularly + * changes the display mode. Where's apparently no way to detect this + * directly from fbcon itself. DRM's mode information might therefore + * be out of data, after it takes over the display at a later time. + * Here, we test the CRTC's current mode with the fbdev state. If they + * do not match, we request a mode change from DRM. If you port an + * fbdev driver to DRM, you can remove this code section, DRM will + * be in full control of the display device and doesn't have to react + * to changes from external sources. + */ + + if (!state->mode_changed && state->adjusted_mode.clock) { + fbdevdrm_init_fb_videomode_from_mode(&fb_mode, &state->adjusted_mode); + fb_var_to_videomode(&fb_var_mode, &modeset->fb_info->var); + if (!fb_mode_is_equal(&fb_mode, &fb_var_mode)) + state->mode_changed = true; + } + + /* TODO: The vblank interupt is currently not supported. We set + * the corresponding flag as a workaround. Some fbdev drivers + * support FBIO_WAITFORVSYNC, which we might use for querying + * vblanks. + * + * DRM porting notes: if you're porting an fbdev driver to DRM, + * remove this line and instead signal a vblank event from the + * interupt handler. + */ + state->no_vblank = true; + return 0; } From patchwork Tue Mar 26 09:17:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 10870701 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3D65615AC for ; Tue, 26 Mar 2019 09:18:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2A82D29037 for ; Tue, 26 Mar 2019 09:18:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1E64529077; Tue, 26 Mar 2019 09:18:00 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 82E1C29037 for ; Tue, 26 Mar 2019 09:17:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729334AbfCZJR7 (ORCPT ); Tue, 26 Mar 2019 05:17:59 -0400 Received: from mx2.suse.de ([195.135.220.15]:39834 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1730624AbfCZJR6 (ORCPT ); Tue, 26 Mar 2019 05:17:58 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id ECD93AE2B; Tue, 26 Mar 2019 09:17:56 +0000 (UTC) From: Thomas Zimmermann To: airlied@linux.ie, daniel@ffwll.ch, b.zolnierkie@samsung.com Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, Thomas Zimmermann Subject: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes Date: Tue, 26 Mar 2019 10:17:44 +0100 Message-Id: <20190326091744.11542-12-tzimmermann@suse.de> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326091744.11542-1-tzimmermann@suse.de> References: <20190326091744.11542-1-tzimmermann@suse.de> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Mode detection currently reports the modes listed in fb_info::modelist. The list is either build from EDID information or, more often, a list of previously set modes. A later update to the mode detection could also take into account the modes in fb_monspecs::modedb or test pre-defined VESA modes. Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 163 +++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c index 87f56ec76edf..e89eca4b58df 100644 --- a/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c @@ -21,9 +21,16 @@ #include #include #include +#include "fbdevdrm_device.h" #include "fbdevdrm_modes.h" #include "fbdevdrm_primary.h" +static struct fbdevdrm_modeset* fbdevdrm_modeset_of_connector( + struct drm_connector *connector) +{ + return container_of(connector, struct fbdevdrm_modeset, connector); +} + static struct fbdevdrm_modeset* fbdevdrm_modeset_of_crtc( struct drm_crtc *crtc) { @@ -353,11 +360,130 @@ static const struct drm_encoder_funcs fbdevdrm_encoder_funcs = { * Connector */ -static int connector_helper_get_modes(struct drm_connector *connector) +static int update_display_info(struct drm_display_info *info, struct fb_info *fb_info) +{ + int num_pixel; + + if (fb_info->fix.type != FB_TYPE_PACKED_PIXELS) + return -EINVAL; /* rule out text mode, etc. */ + + if (fb_info->fix.id[0]) { + strncpy(info->name, fb_info->fix.id, sizeof(info->name) - 1); + info->name[sizeof(info->name) - 1] = '\0'; + } else { + memset(info->name, '\0', sizeof(info->name)); + } + + info->width_mm = fb_info->var.width; + info->height_mm = fb_info->var.height; + info->pixel_clock = PICOS2KHZ(fb_info->var.pixclock); + + num_pixel = 0; + if (fb_info->var.red.length) + ++num_pixel; + if (fb_info->var.green.length) + ++num_pixel; + if (fb_info->var.blue.length) + ++num_pixel; + if (fb_info->var.transp.length) + ++num_pixel; + + if (num_pixel) + info->bpc = fb_info->var.bits_per_pixel; + else + info->bpc = 0; + + info->subpixel_order = SubPixelUnknown; + info->color_formats = DRM_COLOR_FORMAT_RGB444; + info->bus_formats = &info->color_formats; + info->num_bus_formats = 1; + info->bus_flags = 0; + info->max_tmds_clock = 0; + info->dvi_dual = false; + info->has_hdmi_infoframe = false; + info->edid_hdmi_dc_modes = 0; + info->cea_rev = 0; + memset(&info->hdmi, 0, sizeof(info->hdmi)); + info->non_desktop = 0; + + return 0; +} + +static int drm_mode_probed_add_from_fb_videomode( + struct drm_connector *connector, const struct fb_videomode *fb_mode, + struct fb_info *fb_info) { + struct drm_display_mode *mode; + + mode = drm_mode_create_from_fb_videomode(connector->dev, fb_mode); + if (!mode) + return -ENOMEM; + + mode->width_mm = fb_info->var.width; + mode->height_mm = fb_info->var.height; + + drm_mode_probed_add(connector, mode); + + /* update connector properties from display mode */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + connector->interlace_allowed = true; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + connector->doublescan_allowed = true; + if (mode->flags & DRM_MODE_FLAG_3D_MASK) + connector->stereo_allowed = true; + return 0; } +static int connector_helper_get_modes(struct drm_connector *connector) +{ + struct fbdevdrm_modeset *modeset; + struct list_head *pos; + int ret, num_modes = 0; + + modeset = fbdevdrm_modeset_of_connector(connector); + + ret = update_display_info(&connector->display_info, modeset->fb_info); + if (ret) + return 0; + + /* update connector properties from video modes */ + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + connector->stereo_allowed = 0; + + if (!num_modes && modeset->fb_info->mode) { + ret = drm_mode_probed_add_from_fb_videomode( + connector, modeset->fb_info->mode, modeset->fb_info); + if (!ret) + ++num_modes; + } + + if (!num_modes) { + + /* DRM backporting notes: we go through all modes in the + * fb_info's mode list and convert each to a DRM modes. If + * you convert an fbdev driver to DRM, replace this code + * with an actual hardware query. This will usually involve + * reading the monitor EDID via DDC. + */ + + list_for_each(pos, &modeset->fb_info->modelist) { + const struct fb_modelist *modelist = + container_of(pos, struct fb_modelist, list); + + ret = drm_mode_probed_add_from_fb_videomode( + connector, &modelist->mode, + modeset->fb_info); + if (ret < 0) + continue; + ++num_modes; + } + } + + return num_modes; +} + static int connector_helper_detect_ctx(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force) @@ -368,6 +494,21 @@ static int connector_helper_detect_ctx(struct drm_connector *connector, static enum drm_mode_status connector_helper_mode_valid( struct drm_connector *connector, struct drm_display_mode *mode) { + struct fb_var_screeninfo fb_var; + int ret; + struct fbdevdrm_modeset *modeset = fbdevdrm_modeset_of_connector(connector); + + /* fb_validate_mode() requires fb_info->monspecs to contain valid + * data. Skip the test if the maximum clock looks bogus. */ + if (!modeset->fb_info->monspecs.dclkmax) + return MODE_OK; + + fbdevdrm_init_fb_var_screeninfo_from_mode(&fb_var, mode); + + ret = fb_validate_mode(&fb_var, modeset->fb_info); + if (ret < 0) + return MODE_BAD; + return MODE_OK; } @@ -444,6 +585,26 @@ static const struct drm_connector_funcs fbdevdrm_connector_funcs = { static enum drm_mode_status mode_config_mode_valid( struct drm_device *dev, const struct drm_display_mode *mode) { + /* TODO: maybe detect maximum depth */ + static const unsigned int max_bpp = 4; /* 32-bit depth */ + + size_t vram_size_2, fb_size; + struct fbdevdrm_device *fdev = fbdevdrm_device_of_dev(dev); + + /* DRM porting note: The atomic mode-setting framework requires + * two framebuffers to be present in VRAM during page flips. This + * is a problem for modes that require buffers larger than half + * the VRAM size. This can happen on older graphics cards with less + * than 16 MiB of VRAM. There's no point in reporting such modes, + * so we sort them out. If you're porting an fbdevdriver to DRM, you + * may want to keep this policy if the device has limited VRAM. + */ + vram_size_2 = fdev->ttm.vram_size / 2; + + fb_size = mode->hdisplay * mode->vdisplay * max_bpp; + if (fb_size > vram_size_2) + return MODE_MEM; + return MODE_OK; }