All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-26  9:17 ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Hi,

this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
drivers. I'd appreciate feedback on the code and the idea in general.

The fbdev subsystem is considered legacy and will probably be removed at
some point. This would mean the loss of a signifanct number of drivers.
Some of the affected hardware is probably not in use any longer, but some
hardware is still around and provides good(-enough) framebuffers.

OTOH, userspace programs that want to support a wide range of graphics
hardware have to implement support for both DRM and fbdev interfaces. Such
software would benefit from a single interface.

The fbdevdrm driver provides a way of running drivers for old and new
hardware from the DRM subsystem and interfaces.

It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
is supposed to be a template for converting fbdev drivers to DRM. It contains
a number of comments (labeled 'DRM porting note') that explain the required
steps. The license is fairly liberal to allow for combination with existing
fbdev code.

I tested the current patch set with the following drivers: atyfb, aty128fb,
matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
successfully start with fbcon enabled and then run weston or X.

Thomas Zimmermann (11):
  drm/fbdevdrm: Add driver skeleton
  drm/fbdevdrm: Add fbdevdrm device
  drm/fbdevdrm: Add memory management
  drm/fbdevdrm: Add file operations
  drm/fbdevdrm: Add GEM and dumb interfaces
  drm/fbdevdrm: Add modesetting infrastructure
  drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
  drm/fbdevdrm: Add mode conversion DRM <-> fbdev
  drm/fbdevdrm: Add primary plane
  drm/fbdevdrm: Add CRTC
  drm/fbdevdrm: Detect and validate display modes

 drivers/gpu/drm/Kconfig                     |   2 +
 drivers/gpu/drm/Makefile                    |   1 +
 drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
 drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
 19 files changed, 3120 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_bo.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h

--
2.21.0

^ permalink raw reply	[flat|nested] 64+ messages in thread

* [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-26  9:17 ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Hi,

this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
drivers. I'd appreciate feedback on the code and the idea in general.

The fbdev subsystem is considered legacy and will probably be removed at
some point. This would mean the loss of a signifanct number of drivers.
Some of the affected hardware is probably not in use any longer, but some
hardware is still around and provides good(-enough) framebuffers.

OTOH, userspace programs that want to support a wide range of graphics
hardware have to implement support for both DRM and fbdev interfaces. Such
software would benefit from a single interface.

The fbdevdrm driver provides a way of running drivers for old and new
hardware from the DRM subsystem and interfaces.

It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
is supposed to be a template for converting fbdev drivers to DRM. It contains
a number of comments (labeled 'DRM porting note') that explain the required
steps. The license is fairly liberal to allow for combination with existing
fbdev code.

I tested the current patch set with the following drivers: atyfb, aty128fb,
matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
successfully start with fbcon enabled and then run weston or X.

Thomas Zimmermann (11):
  drm/fbdevdrm: Add driver skeleton
  drm/fbdevdrm: Add fbdevdrm device
  drm/fbdevdrm: Add memory management
  drm/fbdevdrm: Add file operations
  drm/fbdevdrm: Add GEM and dumb interfaces
  drm/fbdevdrm: Add modesetting infrastructure
  drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
  drm/fbdevdrm: Add mode conversion DRM <-> fbdev
  drm/fbdevdrm: Add primary plane
  drm/fbdevdrm: Add CRTC
  drm/fbdevdrm: Detect and validate display modes

 drivers/gpu/drm/Kconfig                     |   2 +
 drivers/gpu/drm/Makefile                    |   1 +
 drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
 drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
 19 files changed, 3120 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_bo.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h

--
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* [PATCH 01/11] drm/fbdevdrm: Add driver skeleton
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <linux/fb.h>
+#include <linux/module.h>
+
+/* 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 <tzimmermann@suse.de>"
+#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);
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 01/11] drm/fbdevdrm: Add driver skeleton
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <linux/fb.h>
+#include <linux/module.h>
+
+/* 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 <tzimmermann@suse.de>"
+#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);
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_drv.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+
+/*
+ * 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 <drm/drm_device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+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 <drm/drm_drv.h>
+#include <drm/drm_print.h>
+#include <linux/console.h> /* for console_{un/lock}() */
 #include <linux/fb.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#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)
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_drv.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+
+/*
+ * 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 <drm/drm_device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+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 <drm/drm_drv.h>
+#include <drm/drm_print.h>
+#include <linux/console.h> /* for console_{un/lock}() */
 #include <linux/fb.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#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)
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 03/11] drm/fbdevdrm: Add memory management
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_gem.h>
+#include <drm/ttm/ttm_bo_api.h>
+#include <drm/ttm/ttm_placement.h>
+#include <linux/kernel.h> /* 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 <drm/drm_device.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#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 <drm/drmP.h>
+#include <drm/ttm/ttm_page_alloc.h>
+#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 <drm/ttm/ttm_bo_driver.h>
+
+#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
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 03/11] drm/fbdevdrm: Add memory management
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_gem.h>
+#include <drm/ttm/ttm_bo_api.h>
+#include <drm/ttm/ttm_placement.h>
+#include <linux/kernel.h> /* 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 <drm/drm_device.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#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 <drm/drmP.h>
+#include <drm/ttm/ttm_page_alloc.h>
+#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 <drm/ttm/ttm_bo_driver.h>
+
+#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
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 04/11] drm/fbdevdrm: Add file operations
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
 #include <drm/drm_print.h>
 #include <linux/console.h> /* for console_{un/lock}() */
 #include <linux/fb.h>
@@ -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 */
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 04/11] drm/fbdevdrm: Add file operations
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
 #include <drm/drm_print.h>
 #include <linux/console.h> /* for console_{un/lock}() */
 #include <linux/fb.h>
@@ -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 */
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 05/11] drm/fbdevdrm: Add GEM and dumb interfaces
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <linux/fb.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#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
 };
 
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 05/11] drm/fbdevdrm: Add GEM and dumb interfaces
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <linux/fb.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#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
 };
 
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 06/11] drm/fbdevdrm: Add modesetting infrastructure
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_device.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#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 <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/fb.h>
+
+/*
+ * 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 <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_encoder.h>
+
+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
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 06/11] drm/fbdevdrm: Add modesetting infrastructure
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_device.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#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 <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/fb.h>
+
+/*
+ * 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 <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_encoder.h>
+
+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
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <asm/byteorder.h>
+#include <linux/fb.h>
+
+#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 <drm/drm_fourcc.h>
+
+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
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <asm/byteorder.h>
+#include <linux/fb.h>
+
+#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 <drm/drm_fourcc.h>
+
+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
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 08/11] drm/fbdevdrm: Add mode conversion DRM <-> fbdev
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm_modes.h>
+#include <linux/fb.h>
+
+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
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 08/11] drm/fbdevdrm: Add mode conversion DRM <-> fbdev
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm_modes.h>
+#include <linux/fb.h>
+
+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
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 09/11] drm/fbdevdrm: Add primary plane
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm.h>
+#include <linux/sched.h> /* for TASK_COMM_LEN in <drm/drm_framebuffer.h> */
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_modes.h>
 #include <linux/fb.h>
+#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 <linux/types.h>
+
 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 <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/fb.h>
+#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 <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
 
 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 <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane.h>
+#include <linux/fb.h>
+#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 <linux/types.h>
+
+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
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 09/11] drm/fbdevdrm: Add primary plane
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm.h>
+#include <linux/sched.h> /* for TASK_COMM_LEN in <drm/drm_framebuffer.h> */
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_modes.h>
 #include <linux/fb.h>
+#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 <linux/types.h>
+
 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 <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/fb.h>
+#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 <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
 
 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 <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_plane.h>
+#include <linux/fb.h>
+#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 <linux/types.h>
+
+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
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 10/11] drm/fbdevdrm: Add CRTC
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
 #include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/fb.h>
+#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;
 }
 
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 10/11] drm/fbdevdrm: Add CRTC
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 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 <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
 #include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/fb.h>
+#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;
 }
 
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26  9:17   ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/fb.h>
+#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;
 }
 
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
@ 2019-03-26  9:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26  9:17 UTC (permalink / raw)
  To: airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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 <tzimmermann@suse.de>
---
 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 <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/fb.h>
+#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;
 }
 
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
  2019-03-26  9:17   ` Thomas Zimmermann
@ 2019-03-26 13:33     ` Mathieu Malaterre
  -1 siblings, 0 replies; 64+ messages in thread
From: Mathieu Malaterre @ 2019-03-26 13:33 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Tue, Mar 26, 2019 at 10:18 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  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 <drm/drm.h>
> +#include <linux/sched.h> /* for TASK_COMM_LEN in <drm/drm_framebuffer.h> */
> +#include <drm/drm_framebuffer.h>
>  #include <drm/drm_modes.h>
>  #include <linux/fb.h>
> +#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);

A simple compile/test here leads to:

...
../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
of macro 'do_div'
  do_div(width, cpp);
  ^~~~~~
In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
                 from ../include/linux/kernel.h:18,
                 from ../include/linux/list.h:9,
                 from ../include/linux/rculist.h:10,
                 from ../include/linux/pid.h:5,
                 from ../include/linux/sched.h:14,
                 from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
../include/asm-generic/div64.h:239:22: error: passing argument 1 of
'__div64_32' from incompatible pointer type
[-Werror=incompatible-pointer-types]
   __rem = __div64_32(&(n), __base); \
                      ^~~~
../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
of macro 'do_div'
  do_div(width, cpp);
  ^~~~~~
../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
{aka 'long long unsigned int *'} but argument is of type 'unsigned int
*'
 extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
...

> +
> +       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 <linux/types.h>
> +
>  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 <drm/drm_modeset_helper_vtables.h>
>  #include <drm/drm_probe_helper.h>
>  #include <linux/fb.h>
> +#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 <drm/drm_connector.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_encoder.h>
> +#include <drm/drm_plane.h>
>
>  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 <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <linux/fb.h>
> +#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 <linux/types.h>
> +
> +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
> --
> 2.21.0
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
@ 2019-03-26 13:33     ` Mathieu Malaterre
  0 siblings, 0 replies; 64+ messages in thread
From: Mathieu Malaterre @ 2019-03-26 13:33 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Tue, Mar 26, 2019 at 10:18 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  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 <drm/drm.h>
> +#include <linux/sched.h> /* for TASK_COMM_LEN in <drm/drm_framebuffer.h> */
> +#include <drm/drm_framebuffer.h>
>  #include <drm/drm_modes.h>
>  #include <linux/fb.h>
> +#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);

A simple compile/test here leads to:

...
../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
of macro 'do_div'
  do_div(width, cpp);
  ^~~~~~
In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
                 from ../include/linux/kernel.h:18,
                 from ../include/linux/list.h:9,
                 from ../include/linux/rculist.h:10,
                 from ../include/linux/pid.h:5,
                 from ../include/linux/sched.h:14,
                 from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
../include/asm-generic/div64.h:239:22: error: passing argument 1 of
'__div64_32' from incompatible pointer type
[-Werror=incompatible-pointer-types]
   __rem = __div64_32(&(n), __base); \
                      ^~~~
../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
of macro 'do_div'
  do_div(width, cpp);
  ^~~~~~
../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
{aka 'long long unsigned int *'} but argument is of type 'unsigned int
*'
 extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
...

> +
> +       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 <linux/types.h>
> +
>  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 <drm/drm_modeset_helper_vtables.h>
>  #include <drm/drm_probe_helper.h>
>  #include <linux/fb.h>
> +#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 <drm/drm_connector.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_encoder.h>
> +#include <drm/drm_plane.h>
>
>  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 <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_plane.h>
> +#include <linux/fb.h>
> +#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 <linux/types.h>
> +
> +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
> --
> 2.21.0
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
  2019-03-26 13:33     ` Mathieu Malaterre
@ 2019-03-26 13:57       ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26 13:57 UTC (permalink / raw)
  To: Mathieu Malaterre
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz


[-- Attachment #1.1: Type: text/plain, Size: 26144 bytes --]

Hi

Am 26.03.19 um 14:33 schrieb Mathieu Malaterre:
> A simple compile/test here leads to:
> 
> ...
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
>                  from ../include/linux/kernel.h:18,
>                  from ../include/linux/list.h:9,
>                  from ../include/linux/rculist.h:10,
>                  from ../include/linux/pid.h:5,
>                  from ../include/linux/sched.h:14,
>                  from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
> ../include/asm-generic/div64.h:239:22: error: passing argument 1 of
> '__div64_32' from incompatible pointer type
> [-Werror=incompatible-pointer-types]
>    __rem = __div64_32(&(n), __base); \
>                       ^~~~
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> ../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
> {aka 'long long unsigned int *'} but argument is of type 'unsigned int
> *'
>  extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
> ...

I used a 32-bit machine for testing, so that's probably the difference
here. Anyway, thanks for testing. Will be fixed in the next iteration.

Best regards
Thomas

> 
>> +
>> +       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 <linux/types.h>
>> +
>>  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 <drm/drm_modeset_helper_vtables.h>
>>  #include <drm/drm_probe_helper.h>
>>  #include <linux/fb.h>
>> +#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 <drm/drm_connector.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_encoder.h>
>> +#include <drm/drm_plane.h>
>>
>>  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 <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane.h>
>> +#include <linux/fb.h>
>> +#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 <linux/types.h>
>> +
>> +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
>> --
>> 2.21.0
>>

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
@ 2019-03-26 13:57       ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-26 13:57 UTC (permalink / raw)
  To: Mathieu Malaterre
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz


[-- Attachment #1.1.1: Type: text/plain, Size: 26144 bytes --]

Hi

Am 26.03.19 um 14:33 schrieb Mathieu Malaterre:
> A simple compile/test here leads to:
> 
> ...
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
>                  from ../include/linux/kernel.h:18,
>                  from ../include/linux/list.h:9,
>                  from ../include/linux/rculist.h:10,
>                  from ../include/linux/pid.h:5,
>                  from ../include/linux/sched.h:14,
>                  from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
> ../include/asm-generic/div64.h:239:22: error: passing argument 1 of
> '__div64_32' from incompatible pointer type
> [-Werror=incompatible-pointer-types]
>    __rem = __div64_32(&(n), __base); \
>                       ^~~~
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> ../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
> {aka 'long long unsigned int *'} but argument is of type 'unsigned int
> *'
>  extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
> ...

I used a 32-bit machine for testing, so that's probably the difference
here. Anyway, thanks for testing. Will be fixed in the next iteration.

Best regards
Thomas

> 
>> +
>> +       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 <linux/types.h>
>> +
>>  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 <drm/drm_modeset_helper_vtables.h>
>>  #include <drm/drm_probe_helper.h>
>>  #include <linux/fb.h>
>> +#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 <drm/drm_connector.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_encoder.h>
>> +#include <drm/drm_plane.h>
>>
>>  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 <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane.h>
>> +#include <linux/fb.h>
>> +#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 <linux/types.h>
>> +
>> +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
>> --
>> 2.21.0
>>

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-26  9:17 ` Thomas Zimmermann
@ 2019-03-26 14:53   ` Daniel Vetter
  -1 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-26 14:53 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Tue, Mar 26, 2019 at 10:17:33AM +0100, Thomas Zimmermann wrote:
> Hi,
> 
> this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
> drivers. I'd appreciate feedback on the code and the idea in general.
> 
> The fbdev subsystem is considered legacy and will probably be removed at
> some point. This would mean the loss of a signifanct number of drivers.
> Some of the affected hardware is probably not in use any longer, but some
> hardware is still around and provides good(-enough) framebuffers.
> 
> OTOH, userspace programs that want to support a wide range of graphics
> hardware have to implement support for both DRM and fbdev interfaces. Such
> software would benefit from a single interface.
> 
> The fbdevdrm driver provides a way of running drivers for old and new
> hardware from the DRM subsystem and interfaces.
> 
> It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
> is supposed to be a template for converting fbdev drivers to DRM. It contains
> a number of comments (labeled 'DRM porting note') that explain the required
> steps. The license is fairly liberal to allow for combination with existing
> fbdev code.
> 
> I tested the current patch set with the following drivers: atyfb, aty128fb,
> matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
> successfully start with fbcon enabled and then run weston or X.
> 
> Thomas Zimmermann (11):
>   drm/fbdevdrm: Add driver skeleton
>   drm/fbdevdrm: Add fbdevdrm device
>   drm/fbdevdrm: Add memory management
>   drm/fbdevdrm: Add file operations
>   drm/fbdevdrm: Add GEM and dumb interfaces
>   drm/fbdevdrm: Add modesetting infrastructure
>   drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
>   drm/fbdevdrm: Add mode conversion DRM <-> fbdev
>   drm/fbdevdrm: Add primary plane
>   drm/fbdevdrm: Add CRTC
>   drm/fbdevdrm: Detect and validate display modes

Looks surprisingly clean, at least from a quick read. Only big thing I
noticed on the implementation side is that you probably want to use the
simple display helpers. At least that's a much better fit for simple
display hw supported by these fbdev drivers.

What I'm not sure at all on is whether this is a good idea. It's a quick
way of supporting a few drivers we might need from fbdev. But I fear for
long-term ecosystem health it'll be a loss: Much less motivation to port
the drivers to be native kms ones, and as a result, much less
opportunities to extract helpers to make driver writing for such hw easier
for everyone.

And the latter is really important, if you look at all the work people
have done to improve the modeset helpers. Nowadays we have some examples
where the kms native port of an fbdev driver turned out to be 2-3x
smaller.

But I also see the benefit of making the fbdev->kms transition smoother.
For discussion here's a pretty wild idea that we might want to consider:

- move the ttm based gem code into a helper library like the cma gem
  helpers. Only special piece we need to pass it is the fb_info structure,
  for that we can use the same hooks the cma helpers use to allow drivers
  to specify the right struct device to use for cma allocations. plus ofc
  an init function for the memory manager and all that, which drivers can
  put into their driver private device structure

- move the modeset compat code into a helper based on top of the simple
  display pipe helpers. 

- for each fbdev driver we care about:
  1. create bare-bones kms driver
  2. copypaste the entire fbdev driver code into the drm driver
  3. hook up the above two helper libraries, remove the
  register_framebuffer - I think we can still allocate the fb_info and all
  that for transition
  4. transition the driver over. If we have the helpers a bit split up
  between display and buffer management side that should be a lot more
  gradual, and with the fbdevdrm midlayer inserted in the middle.

- Improve the other helpers we have as needed - there's probably room for
  a simple ttm version, inspired by all these simple display chips (e.g.
  ast/cirrus/bochs/mga all solve similar problems like this).

- Treat any such drivers as CONFIG_STAGING until they've become fully
  native, i.e. if no one bothers to convert them, we'll drop them again.

The above is kinda similar in spirit to the transitional helpers we've
used to upgrade the big drivers from legacy kms to atomic.

Thoughts?

Cheers, Daniel

> 
>  drivers/gpu/drm/Kconfig                     |   2 +
>  drivers/gpu/drm/Makefile                    |   1 +
>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
>  19 files changed, 3120 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_bo.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
> 
> --
> 2.21.0
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-26 14:53   ` Daniel Vetter
  0 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-26 14:53 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Tue, Mar 26, 2019 at 10:17:33AM +0100, Thomas Zimmermann wrote:
> Hi,
> 
> this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
> drivers. I'd appreciate feedback on the code and the idea in general.
> 
> The fbdev subsystem is considered legacy and will probably be removed at
> some point. This would mean the loss of a signifanct number of drivers.
> Some of the affected hardware is probably not in use any longer, but some
> hardware is still around and provides good(-enough) framebuffers.
> 
> OTOH, userspace programs that want to support a wide range of graphics
> hardware have to implement support for both DRM and fbdev interfaces. Such
> software would benefit from a single interface.
> 
> The fbdevdrm driver provides a way of running drivers for old and new
> hardware from the DRM subsystem and interfaces.
> 
> It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
> is supposed to be a template for converting fbdev drivers to DRM. It contains
> a number of comments (labeled 'DRM porting note') that explain the required
> steps. The license is fairly liberal to allow for combination with existing
> fbdev code.
> 
> I tested the current patch set with the following drivers: atyfb, aty128fb,
> matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
> successfully start with fbcon enabled and then run weston or X.
> 
> Thomas Zimmermann (11):
>   drm/fbdevdrm: Add driver skeleton
>   drm/fbdevdrm: Add fbdevdrm device
>   drm/fbdevdrm: Add memory management
>   drm/fbdevdrm: Add file operations
>   drm/fbdevdrm: Add GEM and dumb interfaces
>   drm/fbdevdrm: Add modesetting infrastructure
>   drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
>   drm/fbdevdrm: Add mode conversion DRM <-> fbdev
>   drm/fbdevdrm: Add primary plane
>   drm/fbdevdrm: Add CRTC
>   drm/fbdevdrm: Detect and validate display modes

Looks surprisingly clean, at least from a quick read. Only big thing I
noticed on the implementation side is that you probably want to use the
simple display helpers. At least that's a much better fit for simple
display hw supported by these fbdev drivers.

What I'm not sure at all on is whether this is a good idea. It's a quick
way of supporting a few drivers we might need from fbdev. But I fear for
long-term ecosystem health it'll be a loss: Much less motivation to port
the drivers to be native kms ones, and as a result, much less
opportunities to extract helpers to make driver writing for such hw easier
for everyone.

And the latter is really important, if you look at all the work people
have done to improve the modeset helpers. Nowadays we have some examples
where the kms native port of an fbdev driver turned out to be 2-3x
smaller.

But I also see the benefit of making the fbdev->kms transition smoother.
For discussion here's a pretty wild idea that we might want to consider:

- move the ttm based gem code into a helper library like the cma gem
  helpers. Only special piece we need to pass it is the fb_info structure,
  for that we can use the same hooks the cma helpers use to allow drivers
  to specify the right struct device to use for cma allocations. plus ofc
  an init function for the memory manager and all that, which drivers can
  put into their driver private device structure

- move the modeset compat code into a helper based on top of the simple
  display pipe helpers. 

- for each fbdev driver we care about:
  1. create bare-bones kms driver
  2. copypaste the entire fbdev driver code into the drm driver
  3. hook up the above two helper libraries, remove the
  register_framebuffer - I think we can still allocate the fb_info and all
  that for transition
  4. transition the driver over. If we have the helpers a bit split up
  between display and buffer management side that should be a lot more
  gradual, and with the fbdevdrm midlayer inserted in the middle.

- Improve the other helpers we have as needed - there's probably room for
  a simple ttm version, inspired by all these simple display chips (e.g.
  ast/cirrus/bochs/mga all solve similar problems like this).

- Treat any such drivers as CONFIG_STAGING until they've become fully
  native, i.e. if no one bothers to convert them, we'll drop them again.

The above is kinda similar in spirit to the transitional helpers we've
used to upgrade the big drivers from legacy kms to atomic.

Thoughts?

Cheers, Daniel

> 
>  drivers/gpu/drm/Kconfig                     |   2 +
>  drivers/gpu/drm/Makefile                    |   1 +
>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
>  19 files changed, 3120 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_bo.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
> 
> --
> 2.21.0
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
  2019-03-26  9:17   ` Thomas Zimmermann
@ 2019-03-26 16:03     ` Adam Jackson
  -1 siblings, 0 replies; 64+ messages in thread
From: Adam Jackson @ 2019-03-26 16:03 UTC (permalink / raw)
  To: Thomas Zimmermann, airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, dri-devel

On Tue, 2019-03-26 at 10:17 +0100, Thomas Zimmermann wrote:

> +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");
> +}

This seems odd. s3fb sets fix.accel to NULL unconditionally AFAICT, not
sure why you're testing for that explicitly.

I do have a question though: why _not_ support generic fbdev drivers?
If I had that, and the ability to disable creation of /dev/fb*, I could
expose a consistent video device enumeration to userspace. As it stands
I have no reasonable way of knowing which fbdev and drm devices are
pointed at the same hardware. If there were only drm devices...

- ajax

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
@ 2019-03-26 16:03     ` Adam Jackson
  0 siblings, 0 replies; 64+ messages in thread
From: Adam Jackson @ 2019-03-26 16:03 UTC (permalink / raw)
  To: Thomas Zimmermann, airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, dri-devel

On Tue, 2019-03-26 at 10:17 +0100, Thomas Zimmermann wrote:

> +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");
> +}

This seems odd. s3fb sets fix.accel to NULL unconditionally AFAICT, not
sure why you're testing for that explicitly.

I do have a question though: why _not_ support generic fbdev drivers?
If I had that, and the ability to disable creation of /dev/fb*, I could
expose a consistent video device enumeration to userspace. As it stands
I have no reasonable way of knowing which fbdev and drm devices are
pointed at the same hardware. If there were only drm devices...

- ajax

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
  2019-03-26  9:17   ` Thomas Zimmermann
@ 2019-03-26 16:29     ` Ville Syrjälä
  -1 siblings, 0 replies; 64+ messages in thread
From: Ville Syrjälä @ 2019-03-26 16:29 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Tue, Mar 26, 2019 at 10:17:40AM +0100, Thomas Zimmermann wrote:
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  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 <asm/byteorder.h>
> +#include <linux/fb.h>
> +
> +#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);
> +}

You can't distinguish LE vs. BE like this.

Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?

-- 
Ville Syrjälä
Intel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
@ 2019-03-26 16:29     ` Ville Syrjälä
  0 siblings, 0 replies; 64+ messages in thread
From: Ville Syrjälä @ 2019-03-26 16:29 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Tue, Mar 26, 2019 at 10:17:40AM +0100, Thomas Zimmermann wrote:
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  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 <asm/byteorder.h>
> +#include <linux/fb.h>
> +
> +#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);
> +}

You can't distinguish LE vs. BE like this.

Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?

-- 
Ville Syrjälä
Intel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
  2019-03-26  9:17   ` Thomas Zimmermann
@ 2019-03-26 16:47     ` Ville Syrjälä
  -1 siblings, 0 replies; 64+ messages in thread
From: Ville Syrjälä @ 2019-03-26 16:47 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Tue, Mar 26, 2019 at 10:17:44AM +0100, Thomas Zimmermann wrote:
> 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 <tzimmermann@suse.de>
> ---
>  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 <drm/drm_print.h>
>  #include <drm/drm_probe_helper.h>
>  #include <linux/fb.h>
> +#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;

I think the only things here you may want to set are
width_mm and height_mm. The rest should not matter.

> +
> +	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) {

fbdev has a modelist? I guess it does. But not exposed to
userspace, which is probably the reason I never realized this.

-- 
Ville Syrjälä
Intel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
@ 2019-03-26 16:47     ` Ville Syrjälä
  0 siblings, 0 replies; 64+ messages in thread
From: Ville Syrjälä @ 2019-03-26 16:47 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Tue, Mar 26, 2019 at 10:17:44AM +0100, Thomas Zimmermann wrote:
> 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 <tzimmermann@suse.de>
> ---
>  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 <drm/drm_print.h>
>  #include <drm/drm_probe_helper.h>
>  #include <linux/fb.h>
> +#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;

I think the only things here you may want to set are
width_mm and height_mm. The rest should not matter.

> +
> +	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) {

fbdev has a modelist? I guess it does. But not exposed to
userspace, which is probably the reason I never realized this.

-- 
Ville Syrjälä
Intel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
  2019-03-26 16:47     ` Ville Syrjälä
@ 2019-03-26 18:20       ` Daniel Vetter
  -1 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-26 18:20 UTC (permalink / raw)
  To: Ville Syrjälä
  Cc: linux-fbdev, Thomas Zimmermann, b.zolnierkie, airlied, dri-devel

On Tue, Mar 26, 2019 at 06:47:41PM +0200, Ville Syrjälä wrote:
> On Tue, Mar 26, 2019 at 10:17:44AM +0100, Thomas Zimmermann wrote:
> > 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 <tzimmermann@suse.de>
> > ---
> >  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 <drm/drm_print.h>
> >  #include <drm/drm_probe_helper.h>
> >  #include <linux/fb.h>
> > +#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;
> 
> I think the only things here you may want to set are
> width_mm and height_mm. The rest should not matter.
> 
> > +
> > +	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) {
> 
> fbdev has a modelist? I guess it does. But not exposed to
> userspace, which is probably the reason I never realized this.

It's exposed in sysfs.

Don't ask why I know this, it hurts.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
@ 2019-03-26 18:20       ` Daniel Vetter
  0 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-26 18:20 UTC (permalink / raw)
  To: Ville Syrjälä
  Cc: linux-fbdev, Thomas Zimmermann, b.zolnierkie, airlied, dri-devel

On Tue, Mar 26, 2019 at 06:47:41PM +0200, Ville Syrjälä wrote:
> On Tue, Mar 26, 2019 at 10:17:44AM +0100, Thomas Zimmermann wrote:
> > 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 <tzimmermann@suse.de>
> > ---
> >  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 <drm/drm_print.h>
> >  #include <drm/drm_probe_helper.h>
> >  #include <linux/fb.h>
> > +#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;
> 
> I think the only things here you may want to set are
> width_mm and height_mm. The rest should not matter.
> 
> > +
> > +	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) {
> 
> fbdev has a modelist? I guess it does. But not exposed to
> userspace, which is probably the reason I never realized this.

It's exposed in sysfs.

Don't ask why I know this, it hurts.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
  2019-03-26 16:03     ` Adam Jackson
@ 2019-03-27  7:55       ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  7:55 UTC (permalink / raw)
  To: Adam Jackson, airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2077 bytes --]

Hi

Am 26.03.19 um 17:03 schrieb Adam Jackson:
> On Tue, 2019-03-26 at 10:17 +0100, Thomas Zimmermann wrote:
> 
>> +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");
>> +}
> 
> This seems odd. s3fb sets fix.accel to NULL unconditionally AFAICT, not
> sure why you're testing for that explicitly.

If accel is 0, it might be a generic driver. Hence, fbdevdrm ignored
s3fb, so I added the exception.


> I do have a question though: why _not_ support generic fbdev drivers?
> If I had that, and the ability to disable creation of /dev/fb*, I could
> expose a consistent video device enumeration to userspace. As it stands
> I have no reasonable way of knowing which fbdev and drm devices are
> pointed at the same hardware. If there were only drm devices...

Ignoring generic drivers seemed like the safe bet for now. I found that
vga16fb, vesafb, etc bind to hardware and later get replaced by
HW-specific drivers; DRM drivers with FB emulation should not be handled
by fbdevdrm. So for now, fbdevdrm ignores all this.

This all comes from using the event-reporting mechanism to hook into the
fbdev module. Daniel suggested to copy over some of the fbdev drivers
for porting, which would solve the problem.

Best regards
Thomas

> 
> - ajax
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
@ 2019-03-27  7:55       ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  7:55 UTC (permalink / raw)
  To: Adam Jackson, airlied, daniel, b.zolnierkie; +Cc: linux-fbdev, dri-devel


[-- Attachment #1.1.1: Type: text/plain, Size: 2077 bytes --]

Hi

Am 26.03.19 um 17:03 schrieb Adam Jackson:
> On Tue, 2019-03-26 at 10:17 +0100, Thomas Zimmermann wrote:
> 
>> +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");
>> +}
> 
> This seems odd. s3fb sets fix.accel to NULL unconditionally AFAICT, not
> sure why you're testing for that explicitly.

If accel is 0, it might be a generic driver. Hence, fbdevdrm ignored
s3fb, so I added the exception.


> I do have a question though: why _not_ support generic fbdev drivers?
> If I had that, and the ability to disable creation of /dev/fb*, I could
> expose a consistent video device enumeration to userspace. As it stands
> I have no reasonable way of knowing which fbdev and drm devices are
> pointed at the same hardware. If there were only drm devices...

Ignoring generic drivers seemed like the safe bet for now. I found that
vga16fb, vesafb, etc bind to hardware and later get replaced by
HW-specific drivers; DRM drivers with FB emulation should not be handled
by fbdevdrm. So for now, fbdevdrm ignores all this.

This all comes from using the event-reporting mechanism to hook into the
fbdev module. Daniel suggested to copy over some of the fbdev drivers
for porting, which would solve the problem.

Best regards
Thomas

> 
> - ajax
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
  2019-03-27  7:55       ` Thomas Zimmermann
@ 2019-03-27  8:03         ` Daniel Vetter
  -1 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27  8:03 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 8:55 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
> This all comes from using the event-reporting mechanism to hook into the
> fbdev module. Daniel suggested to copy over some of the fbdev drivers
> for porting, which would solve the problem.

On the fbdev event reporting stuff: I have a patch series to remove
that, and replace the fbdev->fbcon interactions with direct function
calls. The locking in the notifier is a serious pain. I guess that
would be another detail we'd need to change, if we go with this
approach here.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device
@ 2019-03-27  8:03         ` Daniel Vetter
  0 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27  8:03 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 8:55 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
> This all comes from using the event-reporting mechanism to hook into the
> fbdev module. Daniel suggested to copy over some of the fbdev drivers
> for porting, which would solve the problem.

On the fbdev event reporting stuff: I have a patch series to remove
that, and replace the fbdev->fbcon interactions with direct function
calls. The locking in the notifier is a serious pain. I guess that
would be another detail we'd need to change, if we go with this
approach here.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
  2019-03-26 16:29     ` Ville Syrjälä
@ 2019-03-27  8:28       ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  8:28 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie


[-- Attachment #1.1: Type: text/plain, Size: 1549 bytes --]

Hi

Am 26.03.19 um 17:29 schrieb Ville Syrjälä:
>> +
>> +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);
>> +}
> 
> You can't distinguish LE vs. BE like this.
> 
> Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
> 

The test function means 'is the framebuffer in RGB565 format if the host
uses big-endian access'. Further below in the file, there are macros
that reverse the order of fields for little-endian hosts.

However, mapping all this to DRM formats is confusing.

According to the documentation, DRM_FORMAT_ is always in little-endian
format. But does that mean that the device uses little endian or that
the host uses little endian? If the device and the host disagree on
endianess, which takes precedence?

In the end, I tried different combinations of tests and DRM formats, and
checked the resulting output on the screen.

Best regards
Thomas

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
@ 2019-03-27  8:28       ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  8:28 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie


[-- Attachment #1.1.1: Type: text/plain, Size: 1549 bytes --]

Hi

Am 26.03.19 um 17:29 schrieb Ville Syrjälä:
>> +
>> +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);
>> +}
> 
> You can't distinguish LE vs. BE like this.
> 
> Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
> 

The test function means 'is the framebuffer in RGB565 format if the host
uses big-endian access'. Further below in the file, there are macros
that reverse the order of fields for little-endian hosts.

However, mapping all this to DRM formats is confusing.

According to the documentation, DRM_FORMAT_ is always in little-endian
format. But does that mean that the device uses little endian or that
the host uses little endian? If the device and the host disagree on
endianess, which takes precedence?

In the end, I tried different combinations of tests and DRM formats, and
checked the resulting output on the screen.

Best regards
Thomas

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
  2019-03-26 16:47     ` Ville Syrjälä
@ 2019-03-27  8:31       ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  8:31 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie


[-- Attachment #1.1: Type: text/plain, Size: 1796 bytes --]

Hi

Am 26.03.19 um 17:47 schrieb Ville Syrjälä:
>> +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) {
> 
> fbdev has a modelist?

Yes, and its content is surprisingly random! Some drivers fill it with
'real' values coming from DDC probing, some drivers fill it with modes
that have worked before, and some drivers fill it with... something.

Best regards
Thomas

I guess it does. But not exposed to
> userspace, which is probably the reason I never realized this.
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes
@ 2019-03-27  8:31       ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  8:31 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie


[-- Attachment #1.1.1: Type: text/plain, Size: 1796 bytes --]

Hi

Am 26.03.19 um 17:47 schrieb Ville Syrjälä:
>> +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) {
> 
> fbdev has a modelist?

Yes, and its content is surprisingly random! Some drivers fill it with
'real' values coming from DDC probing, some drivers fill it with modes
that have worked before, and some drivers fill it with... something.

Best regards
Thomas

I guess it does. But not exposed to
> userspace, which is probably the reason I never realized this.
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-26 14:53   ` Daniel Vetter
@ 2019-03-27  9:10     ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  9:10 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie


[-- Attachment #1.1: Type: text/plain, Size: 9249 bytes --]

Hi,

first of all, thanks for the detailed feedback.

Am 26.03.19 um 15:53 schrieb Daniel Vetter:
> On Tue, Mar 26, 2019 at 10:17:33AM +0100, Thomas Zimmermann wrote:
>> Hi,
>>
>> this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
>> drivers. I'd appreciate feedback on the code and the idea in general.
>>
>> The fbdev subsystem is considered legacy and will probably be removed at
>> some point. This would mean the loss of a signifanct number of drivers.
>> Some of the affected hardware is probably not in use any longer, but some
>> hardware is still around and provides good(-enough) framebuffers.
>>
>> OTOH, userspace programs that want to support a wide range of graphics
>> hardware have to implement support for both DRM and fbdev interfaces. Such
>> software would benefit from a single interface.
>>
>> The fbdevdrm driver provides a way of running drivers for old and new
>> hardware from the DRM subsystem and interfaces.
>>
>> It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
>> is supposed to be a template for converting fbdev drivers to DRM. It contains
>> a number of comments (labeled 'DRM porting note') that explain the required
>> steps. The license is fairly liberal to allow for combination with existing
>> fbdev code.
>>
>> I tested the current patch set with the following drivers: atyfb, aty128fb,
>> matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
>> successfully start with fbcon enabled and then run weston or X.
>>
>> Thomas Zimmermann (11):
>>   drm/fbdevdrm: Add driver skeleton
>>   drm/fbdevdrm: Add fbdevdrm device
>>   drm/fbdevdrm: Add memory management
>>   drm/fbdevdrm: Add file operations
>>   drm/fbdevdrm: Add GEM and dumb interfaces
>>   drm/fbdevdrm: Add modesetting infrastructure
>>   drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
>>   drm/fbdevdrm: Add mode conversion DRM <-> fbdev
>>   drm/fbdevdrm: Add primary plane
>>   drm/fbdevdrm: Add CRTC
>>   drm/fbdevdrm: Detect and validate display modes
> 
> Looks surprisingly clean, at least from a quick read. Only big thing I
> noticed on the implementation side is that you probably want to use the
> simple display helpers. At least that's a much better fit for simple
> display hw supported by these fbdev drivers.

I thought about using the simple-DRM helpers, but found that a full DRM
driver would be more helpful for porting over fbdev drivers. Unless
simple DRM is a hard requirement, I'd prefer to leave it this way.

For those devices that only support a single pipeline, the conversion to
simple DRM should then be mandatory during the porting process.

> What I'm not sure at all on is whether this is a good idea. It's a quick
> way of supporting a few drivers we might need from fbdev. But I fear for
> long-term ecosystem health it'll be a loss: Much less motivation to port
> the drivers to be native kms ones, and as a result, much less
> opportunities to extract helpers to make driver writing for such hw easier
> for everyone.
> 
> And the latter is really important, if you look at all the work people
> have done to improve the modeset helpers. Nowadays we have some examples
> where the kms native port of an fbdev driver turned out to be 2-3x
> smaller.

I don't disagree with any of this. My intention is to simplify the
graphics stack *without* loosing support for all the old hardware. I see
fbdevdrm as help for porting drivers, or short-term solution for getting
old framebuffer to work.

> But I also see the benefit of making the fbdev->kms transition smoother.
> For discussion here's a pretty wild idea that we might want to consider:
> 
> - move the ttm based gem code into a helper library like the cma gem
>   helpers. Only special piece we need to pass it is the fb_info structure,
>   for that we can use the same hooks the cma helpers use to allow drivers
>   to specify the right struct device to use for cma allocations. plus ofc
>   an init function for the memory manager and all that, which drivers can
>   put into their driver private device structure
> 
> - move the modeset compat code into a helper based on top of the simple
>   display pipe helpers. 
> 
> - for each fbdev driver we care about:
>   1. create bare-bones kms driver
>   2. copypaste the entire fbdev driver code into the drm driver
>   3. hook up the above two helper libraries, remove the
>   register_framebuffer - I think we can still allocate the fb_info and all
>   that for transition
>   4. transition the driver over. If we have the helpers a bit split up
>   between display and buffer management side that should be a lot more
>   gradual, and with the fbdevdrm midlayer inserted in the middle.

I use the current approach because it does not require modifications to
DRM or fbdev. Not copying the fbdev driver code has the advantage that
any fix that goes into fbdev is also used by fbdevdrm.

OTOH, that has some problems. At least the event-reporting hooks appear
to be fragile. You mentioned that you have patches for replacing it. I'd
be happy to use something else. Filtering out generic and DRM-based
drivers is also not optimal.

Instead of adding the porting helpers to the DRM core, I'd suggest to
add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
functionality and provide a replacement for register_framebuffer.

Porting would then mean to

 1) create a new DRM driver by copying fbdevdrm, and
 2) copy the fbdev driver code to the new DRM driver and rename the
function calls accordingly. At this point the new driver should already
work to some extend.
 3) Finally, do the refactoring and get it out of staging.

If the fbdev subsystem is ever going to be removed, the remaining
drivers could be moved under fbdevdrm and have their function calls
adapted. If anyone cares about a driver, it'd be available for
refactoring; otherwise it'd just sit there doing nothing. It's all
self-contained and doesn't pollute the DRM core.

> - Improve the other helpers we have as needed - there's probably room for
>   a simple ttm version, inspired by all these simple display chips (e.g.
>   ast/cirrus/bochs/mga all solve similar problems like this).

Makes sense, but appears to be unrelated.

Best regards
Thomas

> - Treat any such drivers as CONFIG_STAGING until they've become fully
>   native, i.e. if no one bothers to convert them, we'll drop them again.
> 
> The above is kinda similar in spirit to the transitional helpers we've
> used to upgrade the big drivers from legacy kms to atomic.
> 
> Thoughts?
> 
> Cheers, Daniel
> 
>>
>>  drivers/gpu/drm/Kconfig                     |   2 +
>>  drivers/gpu/drm/Makefile                    |   1 +
>>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
>>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
>>  19 files changed, 3120 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_bo.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
>>
>> --
>> 2.21.0
>>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-27  9:10     ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  9:10 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie


[-- Attachment #1.1.1: Type: text/plain, Size: 9249 bytes --]

Hi,

first of all, thanks for the detailed feedback.

Am 26.03.19 um 15:53 schrieb Daniel Vetter:
> On Tue, Mar 26, 2019 at 10:17:33AM +0100, Thomas Zimmermann wrote:
>> Hi,
>>
>> this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
>> drivers. I'd appreciate feedback on the code and the idea in general.
>>
>> The fbdev subsystem is considered legacy and will probably be removed at
>> some point. This would mean the loss of a signifanct number of drivers.
>> Some of the affected hardware is probably not in use any longer, but some
>> hardware is still around and provides good(-enough) framebuffers.
>>
>> OTOH, userspace programs that want to support a wide range of graphics
>> hardware have to implement support for both DRM and fbdev interfaces. Such
>> software would benefit from a single interface.
>>
>> The fbdevdrm driver provides a way of running drivers for old and new
>> hardware from the DRM subsystem and interfaces.
>>
>> It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
>> is supposed to be a template for converting fbdev drivers to DRM. It contains
>> a number of comments (labeled 'DRM porting note') that explain the required
>> steps. The license is fairly liberal to allow for combination with existing
>> fbdev code.
>>
>> I tested the current patch set with the following drivers: atyfb, aty128fb,
>> matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
>> successfully start with fbcon enabled and then run weston or X.
>>
>> Thomas Zimmermann (11):
>>   drm/fbdevdrm: Add driver skeleton
>>   drm/fbdevdrm: Add fbdevdrm device
>>   drm/fbdevdrm: Add memory management
>>   drm/fbdevdrm: Add file operations
>>   drm/fbdevdrm: Add GEM and dumb interfaces
>>   drm/fbdevdrm: Add modesetting infrastructure
>>   drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
>>   drm/fbdevdrm: Add mode conversion DRM <-> fbdev
>>   drm/fbdevdrm: Add primary plane
>>   drm/fbdevdrm: Add CRTC
>>   drm/fbdevdrm: Detect and validate display modes
> 
> Looks surprisingly clean, at least from a quick read. Only big thing I
> noticed on the implementation side is that you probably want to use the
> simple display helpers. At least that's a much better fit for simple
> display hw supported by these fbdev drivers.

I thought about using the simple-DRM helpers, but found that a full DRM
driver would be more helpful for porting over fbdev drivers. Unless
simple DRM is a hard requirement, I'd prefer to leave it this way.

For those devices that only support a single pipeline, the conversion to
simple DRM should then be mandatory during the porting process.

> What I'm not sure at all on is whether this is a good idea. It's a quick
> way of supporting a few drivers we might need from fbdev. But I fear for
> long-term ecosystem health it'll be a loss: Much less motivation to port
> the drivers to be native kms ones, and as a result, much less
> opportunities to extract helpers to make driver writing for such hw easier
> for everyone.
> 
> And the latter is really important, if you look at all the work people
> have done to improve the modeset helpers. Nowadays we have some examples
> where the kms native port of an fbdev driver turned out to be 2-3x
> smaller.

I don't disagree with any of this. My intention is to simplify the
graphics stack *without* loosing support for all the old hardware. I see
fbdevdrm as help for porting drivers, or short-term solution for getting
old framebuffer to work.

> But I also see the benefit of making the fbdev->kms transition smoother.
> For discussion here's a pretty wild idea that we might want to consider:
> 
> - move the ttm based gem code into a helper library like the cma gem
>   helpers. Only special piece we need to pass it is the fb_info structure,
>   for that we can use the same hooks the cma helpers use to allow drivers
>   to specify the right struct device to use for cma allocations. plus ofc
>   an init function for the memory manager and all that, which drivers can
>   put into their driver private device structure
> 
> - move the modeset compat code into a helper based on top of the simple
>   display pipe helpers. 
> 
> - for each fbdev driver we care about:
>   1. create bare-bones kms driver
>   2. copypaste the entire fbdev driver code into the drm driver
>   3. hook up the above two helper libraries, remove the
>   register_framebuffer - I think we can still allocate the fb_info and all
>   that for transition
>   4. transition the driver over. If we have the helpers a bit split up
>   between display and buffer management side that should be a lot more
>   gradual, and with the fbdevdrm midlayer inserted in the middle.

I use the current approach because it does not require modifications to
DRM or fbdev. Not copying the fbdev driver code has the advantage that
any fix that goes into fbdev is also used by fbdevdrm.

OTOH, that has some problems. At least the event-reporting hooks appear
to be fragile. You mentioned that you have patches for replacing it. I'd
be happy to use something else. Filtering out generic and DRM-based
drivers is also not optimal.

Instead of adding the porting helpers to the DRM core, I'd suggest to
add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
functionality and provide a replacement for register_framebuffer.

Porting would then mean to

 1) create a new DRM driver by copying fbdevdrm, and
 2) copy the fbdev driver code to the new DRM driver and rename the
function calls accordingly. At this point the new driver should already
work to some extend.
 3) Finally, do the refactoring and get it out of staging.

If the fbdev subsystem is ever going to be removed, the remaining
drivers could be moved under fbdevdrm and have their function calls
adapted. If anyone cares about a driver, it'd be available for
refactoring; otherwise it'd just sit there doing nothing. It's all
self-contained and doesn't pollute the DRM core.

> - Improve the other helpers we have as needed - there's probably room for
>   a simple ttm version, inspired by all these simple display chips (e.g.
>   ast/cirrus/bochs/mga all solve similar problems like this).

Makes sense, but appears to be unrelated.

Best regards
Thomas

> - Treat any such drivers as CONFIG_STAGING until they've become fully
>   native, i.e. if no one bothers to convert them, we'll drop them again.
> 
> The above is kinda similar in spirit to the transitional helpers we've
> used to upgrade the big drivers from legacy kms to atomic.
> 
> Thoughts?
> 
> Cheers, Daniel
> 
>>
>>  drivers/gpu/drm/Kconfig                     |   2 +
>>  drivers/gpu/drm/Makefile                    |   1 +
>>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
>>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
>>  19 files changed, 3120 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_bo.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
>>
>> --
>> 2.21.0
>>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
  2019-03-26 13:33     ` Mathieu Malaterre
@ 2019-03-27  9:37       ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  9:37 UTC (permalink / raw)
  To: Mathieu Malaterre
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz


[-- Attachment #1.1: Type: text/plain, Size: 26235 bytes --]

Hi

Am 26.03.19 um 14:33 schrieb Mathieu Malaterre:
> 
> ...
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
>                  from ../include/linux/kernel.h:18,
>                  from ../include/linux/list.h:9,
>                  from ../include/linux/rculist.h:10,
>                  from ../include/linux/pid.h:5,
>                  from ../include/linux/sched.h:14,
>                  from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
> ../include/asm-generic/div64.h:239:22: error: passing argument 1 of
> '__div64_32' from incompatible pointer type
> [-Werror=incompatible-pointer-types]
>    __rem = __div64_32(&(n), __base); \
>                       ^~~~
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> ../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
> {aka 'long long unsigned int *'} but argument is of type 'unsigned int
> *'
>  extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
> ...

I didn't see this error in 64-bit builds either. Could you send me your
kernel config? Thanks!

Best regards
Thomas


> 
>> +
>> +       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 <linux/types.h>
>> +
>>  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 <drm/drm_modeset_helper_vtables.h>
>>  #include <drm/drm_probe_helper.h>
>>  #include <linux/fb.h>
>> +#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 <drm/drm_connector.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_encoder.h>
>> +#include <drm/drm_plane.h>
>>
>>  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 <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane.h>
>> +#include <linux/fb.h>
>> +#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 <linux/types.h>
>> +
>> +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
>> --
>> 2.21.0
>>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
@ 2019-03-27  9:37       ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27  9:37 UTC (permalink / raw)
  To: Mathieu Malaterre
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz


[-- Attachment #1.1.1: Type: text/plain, Size: 26235 bytes --]

Hi

Am 26.03.19 um 14:33 schrieb Mathieu Malaterre:
> 
> ...
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
>                  from ../include/linux/kernel.h:18,
>                  from ../include/linux/list.h:9,
>                  from ../include/linux/rculist.h:10,
>                  from ../include/linux/pid.h:5,
>                  from ../include/linux/sched.h:14,
>                  from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
> ../include/asm-generic/div64.h:239:22: error: passing argument 1 of
> '__div64_32' from incompatible pointer type
> [-Werror=incompatible-pointer-types]
>    __rem = __div64_32(&(n), __base); \
>                       ^~~~
> ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> of macro 'do_div'
>   do_div(width, cpp);
>   ^~~~~~
> ../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
> {aka 'long long unsigned int *'} but argument is of type 'unsigned int
> *'
>  extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
> ...

I didn't see this error in 64-bit builds either. Could you send me your
kernel config? Thanks!

Best regards
Thomas


> 
>> +
>> +       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 <linux/types.h>
>> +
>>  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 <drm/drm_modeset_helper_vtables.h>
>>  #include <drm/drm_probe_helper.h>
>>  #include <linux/fb.h>
>> +#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 <drm/drm_connector.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_encoder.h>
>> +#include <drm/drm_plane.h>
>>
>>  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 <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_fourcc.h>
>> +#include <drm/drm_plane.h>
>> +#include <linux/fb.h>
>> +#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 <linux/types.h>
>> +
>> +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
>> --
>> 2.21.0
>>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-27  9:10     ` Thomas Zimmermann
@ 2019-03-27  9:41       ` Daniel Vetter
  -1 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27  9:41 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 10:10 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi,
>
> first of all, thanks for the detailed feedback.
>
> Am 26.03.19 um 15:53 schrieb Daniel Vetter:
> > On Tue, Mar 26, 2019 at 10:17:33AM +0100, Thomas Zimmermann wrote:
> >> Hi,
> >>
> >> this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
> >> drivers. I'd appreciate feedback on the code and the idea in general.
> >>
> >> The fbdev subsystem is considered legacy and will probably be removed at
> >> some point. This would mean the loss of a signifanct number of drivers.
> >> Some of the affected hardware is probably not in use any longer, but some
> >> hardware is still around and provides good(-enough) framebuffers.
> >>
> >> OTOH, userspace programs that want to support a wide range of graphics
> >> hardware have to implement support for both DRM and fbdev interfaces. Such
> >> software would benefit from a single interface.
> >>
> >> The fbdevdrm driver provides a way of running drivers for old and new
> >> hardware from the DRM subsystem and interfaces.
> >>
> >> It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
> >> is supposed to be a template for converting fbdev drivers to DRM. It contains
> >> a number of comments (labeled 'DRM porting note') that explain the required
> >> steps. The license is fairly liberal to allow for combination with existing
> >> fbdev code.
> >>
> >> I tested the current patch set with the following drivers: atyfb, aty128fb,
> >> matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
> >> successfully start with fbcon enabled and then run weston or X.
> >>
> >> Thomas Zimmermann (11):
> >>   drm/fbdevdrm: Add driver skeleton
> >>   drm/fbdevdrm: Add fbdevdrm device
> >>   drm/fbdevdrm: Add memory management
> >>   drm/fbdevdrm: Add file operations
> >>   drm/fbdevdrm: Add GEM and dumb interfaces
> >>   drm/fbdevdrm: Add modesetting infrastructure
> >>   drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
> >>   drm/fbdevdrm: Add mode conversion DRM <-> fbdev
> >>   drm/fbdevdrm: Add primary plane
> >>   drm/fbdevdrm: Add CRTC
> >>   drm/fbdevdrm: Detect and validate display modes
> >
> > Looks surprisingly clean, at least from a quick read. Only big thing I
> > noticed on the implementation side is that you probably want to use the
> > simple display helpers. At least that's a much better fit for simple
> > display hw supported by these fbdev drivers.
>
> I thought about using the simple-DRM helpers, but found that a full DRM
> driver would be more helpful for porting over fbdev drivers. Unless
> simple DRM is a hard requirement, I'd prefer to leave it this way.
>
> For those devices that only support a single pipeline, the conversion to
> simple DRM should then be mandatory during the porting process.

fbdev only supports a single output, all multi-head extensions are
driver specific ioctl hacks. Given that all you can do is switch it on
or off (with a given mode), simple pipe helpers are the perfect fit
for fbdev.

Some drivers might support more (like multiple heads, or at least
different outputs), and those we should convert over. But at least for
step 1 in converting fbdev drivers over, simple pipe is the right
starting point I think.

> > What I'm not sure at all on is whether this is a good idea. It's a quick
> > way of supporting a few drivers we might need from fbdev. But I fear for
> > long-term ecosystem health it'll be a loss: Much less motivation to port
> > the drivers to be native kms ones, and as a result, much less
> > opportunities to extract helpers to make driver writing for such hw easier
> > for everyone.
> >
> > And the latter is really important, if you look at all the work people
> > have done to improve the modeset helpers. Nowadays we have some examples
> > where the kms native port of an fbdev driver turned out to be 2-3x
> > smaller.
>
> I don't disagree with any of this. My intention is to simplify the
> graphics stack *without* loosing support for all the old hardware. I see
> fbdevdrm as help for porting drivers, or short-term solution for getting
> old framebuffer to work.

Simplify for whom? If the goal is to make life easier for userspace
I'm not sure - you already need at least legacy kms and atomic kms
backends (except for the simplest stuff). And I'm not sure how many
lines of code an fbdev backend really is.

Definitely won't simplify things for the kernel, since this way we'll
never know which fbdev drivers are dead and which ones we need to keep
supporting. This isn't the first time we've just dropped a pile of old
hw support on the floor. What we should aim for though is to make the
transition as easy as possible for those few drivers that aren't
ported yet, but still needed.

> > But I also see the benefit of making the fbdev->kms transition smoother.
> > For discussion here's a pretty wild idea that we might want to consider:
> >
> > - move the ttm based gem code into a helper library like the cma gem
> >   helpers. Only special piece we need to pass it is the fb_info structure,
> >   for that we can use the same hooks the cma helpers use to allow drivers
> >   to specify the right struct device to use for cma allocations. plus ofc
> >   an init function for the memory manager and all that, which drivers can
> >   put into their driver private device structure
> >
> > - move the modeset compat code into a helper based on top of the simple
> >   display pipe helpers.
> >
> > - for each fbdev driver we care about:
> >   1. create bare-bones kms driver
> >   2. copypaste the entire fbdev driver code into the drm driver
> >   3. hook up the above two helper libraries, remove the
> >   register_framebuffer - I think we can still allocate the fb_info and all
> >   that for transition
> >   4. transition the driver over. If we have the helpers a bit split up
> >   between display and buffer management side that should be a lot more
> >   gradual, and with the fbdevdrm midlayer inserted in the middle.
>
> I use the current approach because it does not require modifications to
> DRM or fbdev. Not copying the fbdev driver code has the advantage that
> any fix that goes into fbdev is also used by fbdevdrm.
>
> OTOH, that has some problems. At least the event-reporting hooks appear
> to be fragile. You mentioned that you have patches for replacing it. I'd
> be happy to use something else. Filtering out generic and DRM-based
> drivers is also not optimal.
>
> Instead of adding the porting helpers to the DRM core, I'd suggest to
> add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
> functionality and provide a replacement for register_framebuffer.
>
> Porting would then mean to
>
>  1) create a new DRM driver by copying fbdevdrm, and
>  2) copy the fbdev driver code to the new DRM driver and rename the
> function calls accordingly. At this point the new driver should already
> work to some extend.
>  3) Finally, do the refactoring and get it out of staging.

Maybe the idea behind helpers isn't fully clear:

https://blog.ffwll.ch/2016/12/midlayers-once-more-with-feeling.html

The idea with helpers is essentially to have all the flexibility of
copypasting (you can refactor as you see fit), without having to
actually copypaste a lot of the code. That's why I think an fbdevdrm
helper suits, at least as long as we're actively transitioning. Worked
fairly well for the atomic transition at least.

Wrt what you need to provide: I think all that needs to be replaced is
(un)register_framebuffer - all other fbdev functions and
datastructures we should be able to continue to use, even for such a
frankendriver transitioning from fbdev to drm.

> If the fbdev subsystem is ever going to be removed, the remaining
> drivers could be moved under fbdevdrm and have their function calls
> adapted. If anyone cares about a driver, it'd be available for
> refactoring; otherwise it'd just sit there doing nothing. It's all
> self-contained and doesn't pollute the DRM core.

Maybe some confusions:
- We're not going to remove fbdev as long as there's users left. So
this scenario isn't one to optimize for. The only thing that's not
going to happen is adding more features/drivers to fbdev (except
through drm drivers and drm's fbdev compat code).
- I don't think we need to port all drivers over to drm. Most of them
are irrelevant by now, letting them quietly die feels like the best
action. One reason for copypasting drivers to drm was to make sure
there's someone who actually cares enough about that specific driver.

> > - Improve the other helpers we have as needed - there's probably room for
> >   a simple ttm version, inspired by all these simple display chips (e.g.
> >   ast/cirrus/bochs/mga all solve similar problems like this).
>
> Makes sense, but appears to be unrelated.

It's the main reason to have drivers in upstream - sharing code and
infrastructure. At least for me the main benefit in porting a bunch
more fbdev drivers over isn't the old hw support (that hw is quietly
dying anyway, we just have to wait). But improving support for new hw
that fills similar niches, and making it easier for everyone else. Ofc
I'm not going to stop anyone who's going to do all the porting work,
but we can't realistically require it (e.g. legacy kms drivers also
don't support atomic, since simply not possible without rewriting the
driver).

Cheers, Daniel
>
> Best regards
> Thomas
>
> > - Treat any such drivers as CONFIG_STAGING until they've become fully
> >   native, i.e. if no one bothers to convert them, we'll drop them again.
> >
> > The above is kinda similar in spirit to the transitional helpers we've
> > used to upgrade the big drivers from legacy kms to atomic.
> >
> > Thoughts?
> >
> > Cheers, Daniel
> >
> >>
> >>  drivers/gpu/drm/Kconfig                     |   2 +
> >>  drivers/gpu/drm/Makefile                    |   1 +
> >>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
> >>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
> >>  19 files changed, 3120 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_bo.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
> >>
> >> --
> >> 2.21.0
> >>
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> HRB 21284 (AG Nürnberg)
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-27  9:41       ` Daniel Vetter
  0 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27  9:41 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 10:10 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi,
>
> first of all, thanks for the detailed feedback.
>
> Am 26.03.19 um 15:53 schrieb Daniel Vetter:
> > On Tue, Mar 26, 2019 at 10:17:33AM +0100, Thomas Zimmermann wrote:
> >> Hi,
> >>
> >> this RFC patch set implements fbdevdrm, a DRM driver on top of fbdev
> >> drivers. I'd appreciate feedback on the code and the idea in general.
> >>
> >> The fbdev subsystem is considered legacy and will probably be removed at
> >> some point. This would mean the loss of a signifanct number of drivers.
> >> Some of the affected hardware is probably not in use any longer, but some
> >> hardware is still around and provides good(-enough) framebuffers.
> >>
> >> OTOH, userspace programs that want to support a wide range of graphics
> >> hardware have to implement support for both DRM and fbdev interfaces. Such
> >> software would benefit from a single interface.
> >>
> >> The fbdevdrm driver provides a way of running drivers for old and new
> >> hardware from the DRM subsystem and interfaces.
> >>
> >> It's not intended to add new features or drivers to fbdev. Instead fbdevdrm
> >> is supposed to be a template for converting fbdev drivers to DRM. It contains
> >> a number of comments (labeled 'DRM porting note') that explain the required
> >> steps. The license is fairly liberal to allow for combination with existing
> >> fbdev code.
> >>
> >> I tested the current patch set with the following drivers: atyfb, aty128fb,
> >> matroxfb, pm2fb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. I was able to
> >> successfully start with fbcon enabled and then run weston or X.
> >>
> >> Thomas Zimmermann (11):
> >>   drm/fbdevdrm: Add driver skeleton
> >>   drm/fbdevdrm: Add fbdevdrm device
> >>   drm/fbdevdrm: Add memory management
> >>   drm/fbdevdrm: Add file operations
> >>   drm/fbdevdrm: Add GEM and dumb interfaces
> >>   drm/fbdevdrm: Add modesetting infrastructure
> >>   drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
> >>   drm/fbdevdrm: Add mode conversion DRM <-> fbdev
> >>   drm/fbdevdrm: Add primary plane
> >>   drm/fbdevdrm: Add CRTC
> >>   drm/fbdevdrm: Detect and validate display modes
> >
> > Looks surprisingly clean, at least from a quick read. Only big thing I
> > noticed on the implementation side is that you probably want to use the
> > simple display helpers. At least that's a much better fit for simple
> > display hw supported by these fbdev drivers.
>
> I thought about using the simple-DRM helpers, but found that a full DRM
> driver would be more helpful for porting over fbdev drivers. Unless
> simple DRM is a hard requirement, I'd prefer to leave it this way.
>
> For those devices that only support a single pipeline, the conversion to
> simple DRM should then be mandatory during the porting process.

fbdev only supports a single output, all multi-head extensions are
driver specific ioctl hacks. Given that all you can do is switch it on
or off (with a given mode), simple pipe helpers are the perfect fit
for fbdev.

Some drivers might support more (like multiple heads, or at least
different outputs), and those we should convert over. But at least for
step 1 in converting fbdev drivers over, simple pipe is the right
starting point I think.

> > What I'm not sure at all on is whether this is a good idea. It's a quick
> > way of supporting a few drivers we might need from fbdev. But I fear for
> > long-term ecosystem health it'll be a loss: Much less motivation to port
> > the drivers to be native kms ones, and as a result, much less
> > opportunities to extract helpers to make driver writing for such hw easier
> > for everyone.
> >
> > And the latter is really important, if you look at all the work people
> > have done to improve the modeset helpers. Nowadays we have some examples
> > where the kms native port of an fbdev driver turned out to be 2-3x
> > smaller.
>
> I don't disagree with any of this. My intention is to simplify the
> graphics stack *without* loosing support for all the old hardware. I see
> fbdevdrm as help for porting drivers, or short-term solution for getting
> old framebuffer to work.

Simplify for whom? If the goal is to make life easier for userspace
I'm not sure - you already need at least legacy kms and atomic kms
backends (except for the simplest stuff). And I'm not sure how many
lines of code an fbdev backend really is.

Definitely won't simplify things for the kernel, since this way we'll
never know which fbdev drivers are dead and which ones we need to keep
supporting. This isn't the first time we've just dropped a pile of old
hw support on the floor. What we should aim for though is to make the
transition as easy as possible for those few drivers that aren't
ported yet, but still needed.

> > But I also see the benefit of making the fbdev->kms transition smoother.
> > For discussion here's a pretty wild idea that we might want to consider:
> >
> > - move the ttm based gem code into a helper library like the cma gem
> >   helpers. Only special piece we need to pass it is the fb_info structure,
> >   for that we can use the same hooks the cma helpers use to allow drivers
> >   to specify the right struct device to use for cma allocations. plus ofc
> >   an init function for the memory manager and all that, which drivers can
> >   put into their driver private device structure
> >
> > - move the modeset compat code into a helper based on top of the simple
> >   display pipe helpers.
> >
> > - for each fbdev driver we care about:
> >   1. create bare-bones kms driver
> >   2. copypaste the entire fbdev driver code into the drm driver
> >   3. hook up the above two helper libraries, remove the
> >   register_framebuffer - I think we can still allocate the fb_info and all
> >   that for transition
> >   4. transition the driver over. If we have the helpers a bit split up
> >   between display and buffer management side that should be a lot more
> >   gradual, and with the fbdevdrm midlayer inserted in the middle.
>
> I use the current approach because it does not require modifications to
> DRM or fbdev. Not copying the fbdev driver code has the advantage that
> any fix that goes into fbdev is also used by fbdevdrm.
>
> OTOH, that has some problems. At least the event-reporting hooks appear
> to be fragile. You mentioned that you have patches for replacing it. I'd
> be happy to use something else. Filtering out generic and DRM-based
> drivers is also not optimal.
>
> Instead of adding the porting helpers to the DRM core, I'd suggest to
> add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
> functionality and provide a replacement for register_framebuffer.
>
> Porting would then mean to
>
>  1) create a new DRM driver by copying fbdevdrm, and
>  2) copy the fbdev driver code to the new DRM driver and rename the
> function calls accordingly. At this point the new driver should already
> work to some extend.
>  3) Finally, do the refactoring and get it out of staging.

Maybe the idea behind helpers isn't fully clear:

https://blog.ffwll.ch/2016/12/midlayers-once-more-with-feeling.html

The idea with helpers is essentially to have all the flexibility of
copypasting (you can refactor as you see fit), without having to
actually copypaste a lot of the code. That's why I think an fbdevdrm
helper suits, at least as long as we're actively transitioning. Worked
fairly well for the atomic transition at least.

Wrt what you need to provide: I think all that needs to be replaced is
(un)register_framebuffer - all other fbdev functions and
datastructures we should be able to continue to use, even for such a
frankendriver transitioning from fbdev to drm.

> If the fbdev subsystem is ever going to be removed, the remaining
> drivers could be moved under fbdevdrm and have their function calls
> adapted. If anyone cares about a driver, it'd be available for
> refactoring; otherwise it'd just sit there doing nothing. It's all
> self-contained and doesn't pollute the DRM core.

Maybe some confusions:
- We're not going to remove fbdev as long as there's users left. So
this scenario isn't one to optimize for. The only thing that's not
going to happen is adding more features/drivers to fbdev (except
through drm drivers and drm's fbdev compat code).
- I don't think we need to port all drivers over to drm. Most of them
are irrelevant by now, letting them quietly die feels like the best
action. One reason for copypasting drivers to drm was to make sure
there's someone who actually cares enough about that specific driver.

> > - Improve the other helpers we have as needed - there's probably room for
> >   a simple ttm version, inspired by all these simple display chips (e.g.
> >   ast/cirrus/bochs/mga all solve similar problems like this).
>
> Makes sense, but appears to be unrelated.

It's the main reason to have drivers in upstream - sharing code and
infrastructure. At least for me the main benefit in porting a bunch
more fbdev drivers over isn't the old hw support (that hw is quietly
dying anyway, we just have to wait). But improving support for new hw
that fills similar niches, and making it easier for everyone else. Ofc
I'm not going to stop anyone who's going to do all the porting work,
but we can't realistically require it (e.g. legacy kms drivers also
don't support atomic, since simply not possible without rewriting the
driver).

Cheers, Daniel
>
> Best regards
> Thomas
>
> > - Treat any such drivers as CONFIG_STAGING until they've become fully
> >   native, i.e. if no one bothers to convert them, we'll drop them again.
> >
> > The above is kinda similar in spirit to the transitional helpers we've
> > used to upgrade the big drivers from legacy kms to atomic.
> >
> > Thoughts?
> >
> > Cheers, Daniel
> >
> >>
> >>  drivers/gpu/drm/Kconfig                     |   2 +
> >>  drivers/gpu/drm/Makefile                    |   1 +
> >>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
> >>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
> >>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
> >>  19 files changed, 3120 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_bo.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
> >>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
> >>
> >> --
> >> 2.21.0
> >>
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> HRB 21284 (AG Nürnberg)
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-27  9:41       ` Daniel Vetter
@ 2019-03-27  9:55         ` Michel Dänzer
  -1 siblings, 0 replies; 64+ messages in thread
From: Michel Dänzer @ 2019-03-27  9:55 UTC (permalink / raw)
  To: Daniel Vetter, Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On 2019-03-27 10:41 a.m., Daniel Vetter wrote:
> On Wed, Mar 27, 2019 at 10:10 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>> Am 26.03.19 um 15:53 schrieb Daniel Vetter:
>>>
>>> Looks surprisingly clean, at least from a quick read. Only big thing I
>>> noticed on the implementation side is that you probably want to use the
>>> simple display helpers. At least that's a much better fit for simple
>>> display hw supported by these fbdev drivers.
>>
>> I thought about using the simple-DRM helpers, but found that a full DRM
>> driver would be more helpful for porting over fbdev drivers. Unless
>> simple DRM is a hard requirement, I'd prefer to leave it this way.
>>
>> For those devices that only support a single pipeline, the conversion to
>> simple DRM should then be mandatory during the porting process.
> 
> fbdev only supports a single output, all multi-head extensions are
> driver specific ioctl hacks. Given that all you can do is switch it on
> or off (with a given mode), simple pipe helpers are the perfect fit
> for fbdev.
> 
> Some drivers might support more (like multiple heads, or at least
> different outputs), and those we should convert over. But at least for
> step 1 in converting fbdev drivers over, simple pipe is the right
> starting point I think.

Converting an fbdev driver to a KMS one loses some features:

1) Dynamically switching modes / colour formats via fbdev.
2) fbcon acceleration (or maybe this can be preserved?).

In turn, it allows exposing additional functionality:

3) Multiple outputs via KMS.


Is there any benefit in converting a driver without adding 3)? If not,
is simple pipe still a good starting point?


-- 
Earthling Michel Dänzer               |              https://www.amd.com
Libre software enthusiast             |             Mesa and X developer

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-27  9:55         ` Michel Dänzer
  0 siblings, 0 replies; 64+ messages in thread
From: Michel Dänzer @ 2019-03-27  9:55 UTC (permalink / raw)
  To: Daniel Vetter, Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On 2019-03-27 10:41 a.m., Daniel Vetter wrote:
> On Wed, Mar 27, 2019 at 10:10 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>> Am 26.03.19 um 15:53 schrieb Daniel Vetter:
>>>
>>> Looks surprisingly clean, at least from a quick read. Only big thing I
>>> noticed on the implementation side is that you probably want to use the
>>> simple display helpers. At least that's a much better fit for simple
>>> display hw supported by these fbdev drivers.
>>
>> I thought about using the simple-DRM helpers, but found that a full DRM
>> driver would be more helpful for porting over fbdev drivers. Unless
>> simple DRM is a hard requirement, I'd prefer to leave it this way.
>>
>> For those devices that only support a single pipeline, the conversion to
>> simple DRM should then be mandatory during the porting process.
> 
> fbdev only supports a single output, all multi-head extensions are
> driver specific ioctl hacks. Given that all you can do is switch it on
> or off (with a given mode), simple pipe helpers are the perfect fit
> for fbdev.
> 
> Some drivers might support more (like multiple heads, or at least
> different outputs), and those we should convert over. But at least for
> step 1 in converting fbdev drivers over, simple pipe is the right
> starting point I think.

Converting an fbdev driver to a KMS one loses some features:

1) Dynamically switching modes / colour formats via fbdev.
2) fbcon acceleration (or maybe this can be preserved?).

In turn, it allows exposing additional functionality:

3) Multiple outputs via KMS.


Is there any benefit in converting a driver without adding 3)? If not,
is simple pipe still a good starting point?


-- 
Earthling Michel Dänzer               |              https://www.amd.com
Libre software enthusiast             |             Mesa and X developer
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
  2019-03-27  8:28       ` Thomas Zimmermann
@ 2019-03-27 10:00         ` Ville Syrjälä
  -1 siblings, 0 replies; 64+ messages in thread
From: Ville Syrjälä @ 2019-03-27 10:00 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Wed, Mar 27, 2019 at 09:28:49AM +0100, Thomas Zimmermann wrote:
> Hi
> 
> Am 26.03.19 um 17:29 schrieb Ville Syrjälä:
> >> +
> >> +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);
> >> +}
> > 
> > You can't distinguish LE vs. BE like this.
> > 
> > Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
> > 
> 
> The test function means 'is the framebuffer in RGB565 format if the host
> uses big-endian access'. Further below in the file, there are macros
> that reverse the order of fields for little-endian hosts.

And that does not work. Wrong endianness swaps bytes, not components.

> 
> However, mapping all this to DRM formats is confusing.
> 
> According to the documentation, DRM_FORMAT_ is always in little-endian
> format. But does that mean that the device uses little endian or that
> the host uses little endian? If the device and the host disagree on
> endianess, which takes precedence?

It just means "the pixel value listed is stored in memory 
least significant byte first".

> 
> In the end, I tried different combinations of tests and DRM formats, and
> checked the resulting output on the screen.

This was just pointed out to me recently:
https://github.com/afrantzis/pixel-format-guide

It seems to interpret drm formats correctly.

Though I wouldn't mind if someone improved the drm_fourcc.h docs since
everyone except me seems to get confused by the current wording.

-- 
Ville Syrjälä
Intel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion
@ 2019-03-27 10:00         ` Ville Syrjälä
  0 siblings, 0 replies; 64+ messages in thread
From: Ville Syrjälä @ 2019-03-27 10:00 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: airlied, linux-fbdev, dri-devel, b.zolnierkie

On Wed, Mar 27, 2019 at 09:28:49AM +0100, Thomas Zimmermann wrote:
> Hi
> 
> Am 26.03.19 um 17:29 schrieb Ville Syrjälä:
> >> +
> >> +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);
> >> +}
> > 
> > You can't distinguish LE vs. BE like this.
> > 
> > Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
> > 
> 
> The test function means 'is the framebuffer in RGB565 format if the host
> uses big-endian access'. Further below in the file, there are macros
> that reverse the order of fields for little-endian hosts.

And that does not work. Wrong endianness swaps bytes, not components.

> 
> However, mapping all this to DRM formats is confusing.
> 
> According to the documentation, DRM_FORMAT_ is always in little-endian
> format. But does that mean that the device uses little endian or that
> the host uses little endian? If the device and the host disagree on
> endianess, which takes precedence?

It just means "the pixel value listed is stored in memory 
least significant byte first".

> 
> In the end, I tried different combinations of tests and DRM formats, and
> checked the resulting output on the screen.

This was just pointed out to me recently:
https://github.com/afrantzis/pixel-format-guide

It seems to interpret drm formats correctly.

Though I wouldn't mind if someone improved the drm_fourcc.h docs since
everyone except me seems to get confused by the current wording.

-- 
Ville Syrjälä
Intel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-27  9:55         ` Michel Dänzer
@ 2019-03-27 10:58           ` Daniel Vetter
  -1 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27 10:58 UTC (permalink / raw)
  To: Michel Dänzer
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Thomas Zimmermann, Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 10:55 AM Michel Dänzer <michel@daenzer.net> wrote:
>
> On 2019-03-27 10:41 a.m., Daniel Vetter wrote:
> > On Wed, Mar 27, 2019 at 10:10 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
> >> Am 26.03.19 um 15:53 schrieb Daniel Vetter:
> >>>
> >>> Looks surprisingly clean, at least from a quick read. Only big thing I
> >>> noticed on the implementation side is that you probably want to use the
> >>> simple display helpers. At least that's a much better fit for simple
> >>> display hw supported by these fbdev drivers.
> >>
> >> I thought about using the simple-DRM helpers, but found that a full DRM
> >> driver would be more helpful for porting over fbdev drivers. Unless
> >> simple DRM is a hard requirement, I'd prefer to leave it this way.
> >>
> >> For those devices that only support a single pipeline, the conversion to
> >> simple DRM should then be mandatory during the porting process.
> >
> > fbdev only supports a single output, all multi-head extensions are
> > driver specific ioctl hacks. Given that all you can do is switch it on
> > or off (with a given mode), simple pipe helpers are the perfect fit
> > for fbdev.
> >
> > Some drivers might support more (like multiple heads, or at least
> > different outputs), and those we should convert over. But at least for
> > step 1 in converting fbdev drivers over, simple pipe is the right
> > starting point I think.
>
> Converting an fbdev driver to a KMS one loses some features:
>
> 1) Dynamically switching modes / colour formats via fbdev.

Possible to add that to drm_fb_helper.c, it's just after 10+ years
still no one cares, so it didn't happen. We have added a bunch of
fbdev emulation features for stuff people wanted, so patches are
welcome. And will be merged, if they exist.

Ofc will be rather limited since fbdev only has one output. But should
be possible to at least faithfully map for the simple case of only 1
connected output. Ofc we still can't reallocate the fbdev bo, because
fbdev really doesn't like when the aperture moves around. Fixing that
is possible, if you fix the fbdev vs. fbcon locking, which is probably
a few man-years of engineering (I looked at it recently for reasons,
it's totally broken and full of ABBA issues, solved by simply not
taking the other lock).

> 2) fbcon acceleration (or maybe this can be preserved?).

gma500 has fbcon accel. i915 had patches, but wasn't worth it. It's
possible, but demand for this seems approximately nil. We do support
fbdev page flipping, through the overallocate parameter.

> In turn, it allows exposing additional functionality:
>
> 3) Multiple outputs via KMS.
4) multi-master and multi-client (at least if we properly manage the
vram with ttm for discrete, socs should use the cma helpers). I think
that's the big one, and why compositors might want to have kms even
for dumb display hw.
5) on socs: prime buffer sharing, that seems to be the main reason we
gained a few fbdev conversion in kms recently

> Is there any benefit in converting a driver without adding 3)? If not,
> is simple pipe still a good starting point?

simple pipe still allows multiple outputs if they're entirely
separate. Only thing it limits is:
1) only 1 fixed plane per crtc
2) fixed crtc->output routing (but with the option to plug in
drm_bridge transcoders into that fixed pipeline for code sharing).

I still think simple_pipe is a good starting point for most fbdev
drivers left. All the fancy ones that support multiple outputs and
output switching do have at least a basic kms driver already. Since
atomic helpers are fairly modular (and simple pipe builds on top of
atomic helpers) it should be easy to extend from simple pipe to full
atomic once necessary.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-27 10:58           ` Daniel Vetter
  0 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27 10:58 UTC (permalink / raw)
  To: Michel Dänzer
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Thomas Zimmermann, Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 10:55 AM Michel Dänzer <michel@daenzer.net> wrote:
>
> On 2019-03-27 10:41 a.m., Daniel Vetter wrote:
> > On Wed, Mar 27, 2019 at 10:10 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
> >> Am 26.03.19 um 15:53 schrieb Daniel Vetter:
> >>>
> >>> Looks surprisingly clean, at least from a quick read. Only big thing I
> >>> noticed on the implementation side is that you probably want to use the
> >>> simple display helpers. At least that's a much better fit for simple
> >>> display hw supported by these fbdev drivers.
> >>
> >> I thought about using the simple-DRM helpers, but found that a full DRM
> >> driver would be more helpful for porting over fbdev drivers. Unless
> >> simple DRM is a hard requirement, I'd prefer to leave it this way.
> >>
> >> For those devices that only support a single pipeline, the conversion to
> >> simple DRM should then be mandatory during the porting process.
> >
> > fbdev only supports a single output, all multi-head extensions are
> > driver specific ioctl hacks. Given that all you can do is switch it on
> > or off (with a given mode), simple pipe helpers are the perfect fit
> > for fbdev.
> >
> > Some drivers might support more (like multiple heads, or at least
> > different outputs), and those we should convert over. But at least for
> > step 1 in converting fbdev drivers over, simple pipe is the right
> > starting point I think.
>
> Converting an fbdev driver to a KMS one loses some features:
>
> 1) Dynamically switching modes / colour formats via fbdev.

Possible to add that to drm_fb_helper.c, it's just after 10+ years
still no one cares, so it didn't happen. We have added a bunch of
fbdev emulation features for stuff people wanted, so patches are
welcome. And will be merged, if they exist.

Ofc will be rather limited since fbdev only has one output. But should
be possible to at least faithfully map for the simple case of only 1
connected output. Ofc we still can't reallocate the fbdev bo, because
fbdev really doesn't like when the aperture moves around. Fixing that
is possible, if you fix the fbdev vs. fbcon locking, which is probably
a few man-years of engineering (I looked at it recently for reasons,
it's totally broken and full of ABBA issues, solved by simply not
taking the other lock).

> 2) fbcon acceleration (or maybe this can be preserved?).

gma500 has fbcon accel. i915 had patches, but wasn't worth it. It's
possible, but demand for this seems approximately nil. We do support
fbdev page flipping, through the overallocate parameter.

> In turn, it allows exposing additional functionality:
>
> 3) Multiple outputs via KMS.
4) multi-master and multi-client (at least if we properly manage the
vram with ttm for discrete, socs should use the cma helpers). I think
that's the big one, and why compositors might want to have kms even
for dumb display hw.
5) on socs: prime buffer sharing, that seems to be the main reason we
gained a few fbdev conversion in kms recently

> Is there any benefit in converting a driver without adding 3)? If not,
> is simple pipe still a good starting point?

simple pipe still allows multiple outputs if they're entirely
separate. Only thing it limits is:
1) only 1 fixed plane per crtc
2) fixed crtc->output routing (but with the option to plug in
drm_bridge transcoders into that fixed pipeline for code sharing).

I still think simple_pipe is a good starting point for most fbdev
drivers left. All the fancy ones that support multiple outputs and
output switching do have at least a basic kms driver already. Since
atomic helpers are fairly modular (and simple pipe builds on top of
atomic helpers) it should be easy to extend from simple pipe to full
atomic once necessary.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-27  9:41       ` Daniel Vetter
@ 2019-03-27 14:46         ` Thomas Zimmermann
  -1 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27 14:46 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz


[-- Attachment #1.1: Type: text/plain, Size: 6383 bytes --]

Hi

Am 27.03.19 um 10:41 schrieb Daniel Vetter:
>> I use the current approach because it does not require modifications to
>> DRM or fbdev. Not copying the fbdev driver code has the advantage that
>> any fix that goes into fbdev is also used by fbdevdrm.
>>
>> OTOH, that has some problems. At least the event-reporting hooks appear
>> to be fragile. You mentioned that you have patches for replacing it. I'd
>> be happy to use something else. Filtering out generic and DRM-based
>> drivers is also not optimal.
>>
>> Instead of adding the porting helpers to the DRM core, I'd suggest to
>> add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
>> functionality and provide a replacement for register_framebuffer.
>>
>> Porting would then mean to
>>
>>  1) create a new DRM driver by copying fbdevdrm, and
>>  2) copy the fbdev driver code to the new DRM driver and rename the
>> function calls accordingly. At this point the new driver should already
>> work to some extend.
>>  3) Finally, do the refactoring and get it out of staging.
> 
> Maybe the idea behind helpers isn't fully clear:
> 
> https://blog.ffwll.ch/2016/12/midlayers-once-more-with-feeling.html
> 
> The idea with helpers is essentially to have all the flexibility of
> copypasting (you can refactor as you see fit), without having to
> actually copypaste a lot of the code. That's why I think an fbdevdrm
> helper suits, at least as long as we're actively transitioning. Worked
> fairly well for the atomic transition at least.

That's not really a problem from my side, but maybe we misunderstood
each other.

Modeling fbdevdrm after CMA helpers or simple drm would put it next to
central DRM core and helper modules. That seems like an odd place for
transitional helper functions that are supposed to be replaced by the
actual DRM helpers.

I don't want some kind of abstraction layer. I'm proposing a design like
tinydrm, where a separate module offers helpers for fbdev drivers that
are about to be converted.

The difference might not be that significant, but just a question of
were the code is located.


>>> - Improve the other helpers we have as needed - there's probably room for
>>>   a simple ttm version, inspired by all these simple display chips (e.g.
>>>   ast/cirrus/bochs/mga all solve similar problems like this).
>>
>> Makes sense, but appears to be unrelated.
> 
> It's the main reason to have drivers in upstream - sharing code and
> infrastructure.

I meant that creating 'simple TTM' is unrelated to fbdevdrm. It would
make sense in any case.

Best regards
Thomas

At least for me the main benefit in porting a bunch
> more fbdev drivers over isn't the old hw support (that hw is quietly
> dying anyway, we just have to wait). But improving support for new hw
> that fills similar niches, and making it easier for everyone else. Ofc
> I'm not going to stop anyone who's going to do all the porting work,
> but we can't realistically require it (e.g. legacy kms drivers also
> don't support atomic, since simply not possible without rewriting the
> driver).
> 
> Cheers, Daniel
>>
>> Best regards
>> Thomas
>>
>>> - Treat any such drivers as CONFIG_STAGING until they've become fully
>>>   native, i.e. if no one bothers to convert them, we'll drop them again.
>>>
>>> The above is kinda similar in spirit to the transitional helpers we've
>>> used to upgrade the big drivers from legacy kms to atomic.
>>>
>>> Thoughts?
>>>
>>> Cheers, Daniel
>>>
>>>>
>>>>  drivers/gpu/drm/Kconfig                     |   2 +
>>>>  drivers/gpu/drm/Makefile                    |   1 +
>>>>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
>>>>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
>>>>  19 files changed, 3120 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_bo.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
>>>>
>>>> --
>>>> 2.21.0
>>>>
>>>
>>
>> --
>> Thomas Zimmermann
>> Graphics Driver Developer
>> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
>> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
>> HRB 21284 (AG Nürnberg)
>>
> 
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-27 14:46         ` Thomas Zimmermann
  0 siblings, 0 replies; 64+ messages in thread
From: Thomas Zimmermann @ 2019-03-27 14:46 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz


[-- Attachment #1.1.1: Type: text/plain, Size: 6383 bytes --]

Hi

Am 27.03.19 um 10:41 schrieb Daniel Vetter:
>> I use the current approach because it does not require modifications to
>> DRM or fbdev. Not copying the fbdev driver code has the advantage that
>> any fix that goes into fbdev is also used by fbdevdrm.
>>
>> OTOH, that has some problems. At least the event-reporting hooks appear
>> to be fragile. You mentioned that you have patches for replacing it. I'd
>> be happy to use something else. Filtering out generic and DRM-based
>> drivers is also not optimal.
>>
>> Instead of adding the porting helpers to the DRM core, I'd suggest to
>> add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
>> functionality and provide a replacement for register_framebuffer.
>>
>> Porting would then mean to
>>
>>  1) create a new DRM driver by copying fbdevdrm, and
>>  2) copy the fbdev driver code to the new DRM driver and rename the
>> function calls accordingly. At this point the new driver should already
>> work to some extend.
>>  3) Finally, do the refactoring and get it out of staging.
> 
> Maybe the idea behind helpers isn't fully clear:
> 
> https://blog.ffwll.ch/2016/12/midlayers-once-more-with-feeling.html
> 
> The idea with helpers is essentially to have all the flexibility of
> copypasting (you can refactor as you see fit), without having to
> actually copypaste a lot of the code. That's why I think an fbdevdrm
> helper suits, at least as long as we're actively transitioning. Worked
> fairly well for the atomic transition at least.

That's not really a problem from my side, but maybe we misunderstood
each other.

Modeling fbdevdrm after CMA helpers or simple drm would put it next to
central DRM core and helper modules. That seems like an odd place for
transitional helper functions that are supposed to be replaced by the
actual DRM helpers.

I don't want some kind of abstraction layer. I'm proposing a design like
tinydrm, where a separate module offers helpers for fbdev drivers that
are about to be converted.

The difference might not be that significant, but just a question of
were the code is located.


>>> - Improve the other helpers we have as needed - there's probably room for
>>>   a simple ttm version, inspired by all these simple display chips (e.g.
>>>   ast/cirrus/bochs/mga all solve similar problems like this).
>>
>> Makes sense, but appears to be unrelated.
> 
> It's the main reason to have drivers in upstream - sharing code and
> infrastructure.

I meant that creating 'simple TTM' is unrelated to fbdevdrm. It would
make sense in any case.

Best regards
Thomas

At least for me the main benefit in porting a bunch
> more fbdev drivers over isn't the old hw support (that hw is quietly
> dying anyway, we just have to wait). But improving support for new hw
> that fills similar niches, and making it easier for everyone else. Ofc
> I'm not going to stop anyone who's going to do all the porting work,
> but we can't realistically require it (e.g. legacy kms drivers also
> don't support atomic, since simply not possible without rewriting the
> driver).
> 
> Cheers, Daniel
>>
>> Best regards
>> Thomas
>>
>>> - Treat any such drivers as CONFIG_STAGING until they've become fully
>>>   native, i.e. if no one bothers to convert them, we'll drop them again.
>>>
>>> The above is kinda similar in spirit to the transitional helpers we've
>>> used to upgrade the big drivers from legacy kms to atomic.
>>>
>>> Thoughts?
>>>
>>> Cheers, Daniel
>>>
>>>>
>>>>  drivers/gpu/drm/Kconfig                     |   2 +
>>>>  drivers/gpu/drm/Makefile                    |   1 +
>>>>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
>>>>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
>>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
>>>>  19 files changed, 3120 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_bo.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
>>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
>>>>
>>>> --
>>>> 2.21.0
>>>>
>>>
>>
>> --
>> Thomas Zimmermann
>> Graphics Driver Developer
>> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
>> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
>> HRB 21284 (AG Nürnberg)
>>
> 
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
  2019-03-27 14:46         ` Thomas Zimmermann
@ 2019-03-27 17:05           ` Daniel Vetter
  -1 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27 17:05 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 3:46 PM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi
>
> Am 27.03.19 um 10:41 schrieb Daniel Vetter:
> >> I use the current approach because it does not require modifications to
> >> DRM or fbdev. Not copying the fbdev driver code has the advantage that
> >> any fix that goes into fbdev is also used by fbdevdrm.
> >>
> >> OTOH, that has some problems. At least the event-reporting hooks appear
> >> to be fragile. You mentioned that you have patches for replacing it. I'd
> >> be happy to use something else. Filtering out generic and DRM-based
> >> drivers is also not optimal.
> >>
> >> Instead of adding the porting helpers to the DRM core, I'd suggest to
> >> add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
> >> functionality and provide a replacement for register_framebuffer.
> >>
> >> Porting would then mean to
> >>
> >>  1) create a new DRM driver by copying fbdevdrm, and
> >>  2) copy the fbdev driver code to the new DRM driver and rename the
> >> function calls accordingly. At this point the new driver should already
> >> work to some extend.
> >>  3) Finally, do the refactoring and get it out of staging.
> >
> > Maybe the idea behind helpers isn't fully clear:
> >
> > https://blog.ffwll.ch/2016/12/midlayers-once-more-with-feeling.html
> >
> > The idea with helpers is essentially to have all the flexibility of
> > copypasting (you can refactor as you see fit), without having to
> > actually copypaste a lot of the code. That's why I think an fbdevdrm
> > helper suits, at least as long as we're actively transitioning. Worked
> > fairly well for the atomic transition at least.
>
> That's not really a problem from my side, but maybe we misunderstood
> each other.
>
> Modeling fbdevdrm after CMA helpers or simple drm would put it next to
> central DRM core and helper modules. That seems like an odd place for
> transitional helper functions that are supposed to be replaced by the
> actual DRM helpers.

That's exactly what we've done with the transitional atomic helpers
too. I think that's perfectly fine, as long as we put a solid
explanation next to the code what it is all about and how to use it.

> I don't want some kind of abstraction layer. I'm proposing a design like
> tinydrm, where a separate module offers helpers for fbdev drivers that
> are about to be converted.

We merged tinydrm because reworking it all into proper helpers took a
bit longer than what's reasonable to do out-of-tree. But it's
disappearing (the very last patch series is pending merge I think
now). It worked out well in the end, but I don't think it's a good
model to emulate. There's even a patch to rename the directory to make
it clear they're full drm drivers, just small one-file ones.

Having an entire driver sit in between the actual driver and drm core
feels a lot like a midlayer, and we try very hard to avoid those.
-Daniel

> The difference might not be that significant, but just a question of
> were the code is located.
>
>
> >>> - Improve the other helpers we have as needed - there's probably room for
> >>>   a simple ttm version, inspired by all these simple display chips (e.g.
> >>>   ast/cirrus/bochs/mga all solve similar problems like this).
> >>
> >> Makes sense, but appears to be unrelated.
> >
> > It's the main reason to have drivers in upstream - sharing code and
> > infrastructure.
>
> I meant that creating 'simple TTM' is unrelated to fbdevdrm. It would
> make sense in any case.
>
> Best regards
> Thomas
>
> At least for me the main benefit in porting a bunch
> > more fbdev drivers over isn't the old hw support (that hw is quietly
> > dying anyway, we just have to wait). But improving support for new hw
> > that fills similar niches, and making it easier for everyone else. Ofc
> > I'm not going to stop anyone who's going to do all the porting work,
> > but we can't realistically require it (e.g. legacy kms drivers also
> > don't support atomic, since simply not possible without rewriting the
> > driver).
> >
> > Cheers, Daniel
> >>
> >> Best regards
> >> Thomas
> >>
> >>> - Treat any such drivers as CONFIG_STAGING until they've become fully
> >>>   native, i.e. if no one bothers to convert them, we'll drop them again.
> >>>
> >>> The above is kinda similar in spirit to the transitional helpers we've
> >>> used to upgrade the big drivers from legacy kms to atomic.
> >>>
> >>> Thoughts?
> >>>
> >>> Cheers, Daniel
> >>>
> >>>>
> >>>>  drivers/gpu/drm/Kconfig                     |   2 +
> >>>>  drivers/gpu/drm/Makefile                    |   1 +
> >>>>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
> >>>>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
> >>>>  19 files changed, 3120 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_bo.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
> >>>>
> >>>> --
> >>>> 2.21.0
> >>>>
> >>>
> >>
> >> --
> >> Thomas Zimmermann
> >> Graphics Driver Developer
> >> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> >> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> >> HRB 21284 (AG Nürnberg)
> >>
> >
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> HRB 21284 (AG Nürnberg)
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [RFC][PATCH 00/11] DRM driver for fbdev devices
@ 2019-03-27 17:05           ` Daniel Vetter
  0 siblings, 0 replies; 64+ messages in thread
From: Daniel Vetter @ 2019-03-27 17:05 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: Dave Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 3:46 PM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi
>
> Am 27.03.19 um 10:41 schrieb Daniel Vetter:
> >> I use the current approach because it does not require modifications to
> >> DRM or fbdev. Not copying the fbdev driver code has the advantage that
> >> any fix that goes into fbdev is also used by fbdevdrm.
> >>
> >> OTOH, that has some problems. At least the event-reporting hooks appear
> >> to be fragile. You mentioned that you have patches for replacing it. I'd
> >> be happy to use something else. Filtering out generic and DRM-based
> >> drivers is also not optimal.
> >>
> >> Instead of adding the porting helpers to the DRM core, I'd suggest to
> >> add them to fbdevdrm. Fbdevdrm would have to duplicate some of the fbdev
> >> functionality and provide a replacement for register_framebuffer.
> >>
> >> Porting would then mean to
> >>
> >>  1) create a new DRM driver by copying fbdevdrm, and
> >>  2) copy the fbdev driver code to the new DRM driver and rename the
> >> function calls accordingly. At this point the new driver should already
> >> work to some extend.
> >>  3) Finally, do the refactoring and get it out of staging.
> >
> > Maybe the idea behind helpers isn't fully clear:
> >
> > https://blog.ffwll.ch/2016/12/midlayers-once-more-with-feeling.html
> >
> > The idea with helpers is essentially to have all the flexibility of
> > copypasting (you can refactor as you see fit), without having to
> > actually copypaste a lot of the code. That's why I think an fbdevdrm
> > helper suits, at least as long as we're actively transitioning. Worked
> > fairly well for the atomic transition at least.
>
> That's not really a problem from my side, but maybe we misunderstood
> each other.
>
> Modeling fbdevdrm after CMA helpers or simple drm would put it next to
> central DRM core and helper modules. That seems like an odd place for
> transitional helper functions that are supposed to be replaced by the
> actual DRM helpers.

That's exactly what we've done with the transitional atomic helpers
too. I think that's perfectly fine, as long as we put a solid
explanation next to the code what it is all about and how to use it.

> I don't want some kind of abstraction layer. I'm proposing a design like
> tinydrm, where a separate module offers helpers for fbdev drivers that
> are about to be converted.

We merged tinydrm because reworking it all into proper helpers took a
bit longer than what's reasonable to do out-of-tree. But it's
disappearing (the very last patch series is pending merge I think
now). It worked out well in the end, but I don't think it's a good
model to emulate. There's even a patch to rename the directory to make
it clear they're full drm drivers, just small one-file ones.

Having an entire driver sit in between the actual driver and drm core
feels a lot like a midlayer, and we try very hard to avoid those.
-Daniel

> The difference might not be that significant, but just a question of
> were the code is located.
>
>
> >>> - Improve the other helpers we have as needed - there's probably room for
> >>>   a simple ttm version, inspired by all these simple display chips (e.g.
> >>>   ast/cirrus/bochs/mga all solve similar problems like this).
> >>
> >> Makes sense, but appears to be unrelated.
> >
> > It's the main reason to have drivers in upstream - sharing code and
> > infrastructure.
>
> I meant that creating 'simple TTM' is unrelated to fbdevdrm. It would
> make sense in any case.
>
> Best regards
> Thomas
>
> At least for me the main benefit in porting a bunch
> > more fbdev drivers over isn't the old hw support (that hw is quietly
> > dying anyway, we just have to wait). But improving support for new hw
> > that fills similar niches, and making it easier for everyone else. Ofc
> > I'm not going to stop anyone who's going to do all the porting work,
> > but we can't realistically require it (e.g. legacy kms drivers also
> > don't support atomic, since simply not possible without rewriting the
> > driver).
> >
> > Cheers, Daniel
> >>
> >> Best regards
> >> Thomas
> >>
> >>> - Treat any such drivers as CONFIG_STAGING until they've become fully
> >>>   native, i.e. if no one bothers to convert them, we'll drop them again.
> >>>
> >>> The above is kinda similar in spirit to the transitional helpers we've
> >>> used to upgrade the big drivers from legacy kms to atomic.
> >>>
> >>> Thoughts?
> >>>
> >>> Cheers, Daniel
> >>>
> >>>>
> >>>>  drivers/gpu/drm/Kconfig                     |   2 +
> >>>>  drivers/gpu/drm/Makefile                    |   1 +
> >>>>  drivers/gpu/drm/fbdevdrm/Kconfig            |  13 +
> >>>>  drivers/gpu/drm/fbdevdrm/Makefile           |  11 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.c      | 276 ++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h      |  58 ++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c  |  96 +++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h  |  55 ++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c     | 347 +++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c  | 441 ++++++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h  |  26 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c   | 195 +++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h   |  53 ++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c | 746 ++++++++++++++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h |  38 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c | 498 +++++++++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h |  27 +
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c     | 202 ++++++
> >>>>  drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h     |  35 +
> >>>>  19 files changed, 3120 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_bo.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_bo.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_device.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_drv.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_modeset.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_primary.h
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.c
> >>>>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_ttm.h
> >>>>
> >>>> --
> >>>> 2.21.0
> >>>>
> >>>
> >>
> >> --
> >> Thomas Zimmermann
> >> Graphics Driver Developer
> >> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> >> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> >> HRB 21284 (AG Nürnberg)
> >>
> >
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> HRB 21284 (AG Nürnberg)
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
  2019-03-27  9:37       ` Thomas Zimmermann
@ 2019-04-02  7:08         ` Mathieu Malaterre
  -1 siblings, 0 replies; 64+ messages in thread
From: Mathieu Malaterre @ 2019-04-02  7:08 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 10:37 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi
>
> Am 26.03.19 um 14:33 schrieb Mathieu Malaterre:
> >
> > ...
> > ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> > of macro 'do_div'
> >   do_div(width, cpp);
> >   ^~~~~~
> > In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
> >                  from ../include/linux/kernel.h:18,
> >                  from ../include/linux/list.h:9,
> >                  from ../include/linux/rculist.h:10,
> >                  from ../include/linux/pid.h:5,
> >                  from ../include/linux/sched.h:14,
> >                  from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
> > ../include/asm-generic/div64.h:239:22: error: passing argument 1 of
> > '__div64_32' from incompatible pointer type
> > [-Werror=incompatible-pointer-types]
> >    __rem = __div64_32(&(n), __base); \
> >                       ^~~~
> > ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> > of macro 'do_div'
> >   do_div(width, cpp);
> >   ^~~~~~
> > ../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
> > {aka 'long long unsigned int *'} but argument is of type 'unsigned int
> > *'
> >  extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
> > ...
>
> I didn't see this error in 64-bit builds either. Could you send me your
> kernel config? Thanks!

https://github.com/malaterre/linux/blob/g4/arch/powerpc/configs/g4_defconfig

I am using Debian/sid with the default cross compiler for powerpc:

$ make -j8 O=g4 ARCH=powerpc CROSS_COMPILE=powerpc-linux-gnu-

> Best regards
> Thomas
>
>
> >
> >> +
> >> +       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 <linux/types.h>
> >> +
> >>  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 <drm/drm_modeset_helper_vtables.h>
> >>  #include <drm/drm_probe_helper.h>
> >>  #include <linux/fb.h>
> >> +#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 <drm/drm_connector.h>
> >>  #include <drm/drm_crtc.h>
> >>  #include <drm/drm_encoder.h>
> >> +#include <drm/drm_plane.h>
> >>
> >>  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 <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_plane.h>
> >> +#include <linux/fb.h>
> >> +#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 <linux/types.h>
> >> +
> >> +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
> >> --
> >> 2.21.0
> >>
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> HRB 21284 (AG Nürnberg)
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/11] drm/fbdevdrm: Add primary plane
@ 2019-04-02  7:08         ` Mathieu Malaterre
  0 siblings, 0 replies; 64+ messages in thread
From: Mathieu Malaterre @ 2019-04-02  7:08 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: David Airlie, Linux Fbdev development list, dri-devel,
	Bartlomiej Zolnierkiewicz

On Wed, Mar 27, 2019 at 10:37 AM Thomas Zimmermann <tzimmermann@suse.de> wrote:
>
> Hi
>
> Am 26.03.19 um 14:33 schrieb Mathieu Malaterre:
> >
> > ...
> > ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> > of macro 'do_div'
> >   do_div(width, cpp);
> >   ^~~~~~
> > In file included from ./arch/powerpc/include/generated/asm/div64.h:1,
> >                  from ../include/linux/kernel.h:18,
> >                  from ../include/linux/list.h:9,
> >                  from ../include/linux/rculist.h:10,
> >                  from ../include/linux/pid.h:5,
> >                  from ../include/linux/sched.h:14,
> >                  from ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:15:
> > ../include/asm-generic/div64.h:239:22: error: passing argument 1 of
> > '__div64_32' from incompatible pointer type
> > [-Werror=incompatible-pointer-types]
> >    __rem = __div64_32(&(n), __base); \
> >                       ^~~~
> > ../drivers/gpu/drm/fbdevdrm/fbdevdrm_modes.c:174:2: note: in expansion
> > of macro 'do_div'
> >   do_div(width, cpp);
> >   ^~~~~~
> > ../include/asm-generic/div64.h:213:38: note: expected 'uint64_t *'
> > {aka 'long long unsigned int *'} but argument is of type 'unsigned int
> > *'
> >  extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
> > ...
>
> I didn't see this error in 64-bit builds either. Could you send me your
> kernel config? Thanks!

https://github.com/malaterre/linux/blob/g4/arch/powerpc/configs/g4_defconfig

I am using Debian/sid with the default cross compiler for powerpc:

$ make -j8 O=g4 ARCH=powerpc CROSS_COMPILE=powerpc-linux-gnu-

> Best regards
> Thomas
>
>
> >
> >> +
> >> +       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 <linux/types.h>
> >> +
> >>  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 <drm/drm_modeset_helper_vtables.h>
> >>  #include <drm/drm_probe_helper.h>
> >>  #include <linux/fb.h>
> >> +#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 <drm/drm_connector.h>
> >>  #include <drm/drm_crtc.h>
> >>  #include <drm/drm_encoder.h>
> >> +#include <drm/drm_plane.h>
> >>
> >>  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 <drm/drm_atomic.h>
> >> +#include <drm/drm_atomic_helper.h>
> >> +#include <drm/drm_fourcc.h>
> >> +#include <drm/drm_plane.h>
> >> +#include <linux/fb.h>
> >> +#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 <linux/types.h>
> >> +
> >> +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
> >> --
> >> 2.21.0
> >>
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
>
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
> HRB 21284 (AG Nürnberg)
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 64+ messages in thread

end of thread, other threads:[~2019-04-02  7:09 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-26  9:17 [RFC][PATCH 00/11] DRM driver for fbdev devices Thomas Zimmermann
2019-03-26  9:17 ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 01/11] drm/fbdevdrm: Add driver skeleton Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 02/11] drm/fbdevdrm: Add fbdevdrm device Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26 16:03   ` Adam Jackson
2019-03-26 16:03     ` Adam Jackson
2019-03-27  7:55     ` Thomas Zimmermann
2019-03-27  7:55       ` Thomas Zimmermann
2019-03-27  8:03       ` Daniel Vetter
2019-03-27  8:03         ` Daniel Vetter
2019-03-26  9:17 ` [PATCH 03/11] drm/fbdevdrm: Add memory management Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 04/11] drm/fbdevdrm: Add file operations Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 05/11] drm/fbdevdrm: Add GEM and dumb interfaces Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 06/11] drm/fbdevdrm: Add modesetting infrastructure Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26 16:29   ` Ville Syrjälä
2019-03-26 16:29     ` Ville Syrjälä
2019-03-27  8:28     ` Thomas Zimmermann
2019-03-27  8:28       ` Thomas Zimmermann
2019-03-27 10:00       ` Ville Syrjälä
2019-03-27 10:00         ` Ville Syrjälä
2019-03-26  9:17 ` [PATCH 08/11] drm/fbdevdrm: Add mode conversion DRM <-> fbdev Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 09/11] drm/fbdevdrm: Add primary plane Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26 13:33   ` Mathieu Malaterre
2019-03-26 13:33     ` Mathieu Malaterre
2019-03-26 13:57     ` Thomas Zimmermann
2019-03-26 13:57       ` Thomas Zimmermann
2019-03-27  9:37     ` Thomas Zimmermann
2019-03-27  9:37       ` Thomas Zimmermann
2019-04-02  7:08       ` Mathieu Malaterre
2019-04-02  7:08         ` Mathieu Malaterre
2019-03-26  9:17 ` [PATCH 10/11] drm/fbdevdrm: Add CRTC Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26  9:17 ` [PATCH 11/11] drm/fbdevdrm: Detect and validate display modes Thomas Zimmermann
2019-03-26  9:17   ` Thomas Zimmermann
2019-03-26 16:47   ` Ville Syrjälä
2019-03-26 16:47     ` Ville Syrjälä
2019-03-26 18:20     ` Daniel Vetter
2019-03-26 18:20       ` Daniel Vetter
2019-03-27  8:31     ` Thomas Zimmermann
2019-03-27  8:31       ` Thomas Zimmermann
2019-03-26 14:53 ` [RFC][PATCH 00/11] DRM driver for fbdev devices Daniel Vetter
2019-03-26 14:53   ` Daniel Vetter
2019-03-27  9:10   ` Thomas Zimmermann
2019-03-27  9:10     ` Thomas Zimmermann
2019-03-27  9:41     ` Daniel Vetter
2019-03-27  9:41       ` Daniel Vetter
2019-03-27  9:55       ` Michel Dänzer
2019-03-27  9:55         ` Michel Dänzer
2019-03-27 10:58         ` Daniel Vetter
2019-03-27 10:58           ` Daniel Vetter
2019-03-27 14:46       ` Thomas Zimmermann
2019-03-27 14:46         ` Thomas Zimmermann
2019-03-27 17:05         ` Daniel Vetter
2019-03-27 17:05           ` Daniel Vetter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.