All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Noralf Trønnes" <noralf@tronnes.org>
To: dri-devel@lists.freedesktop.org
Cc: intel-gfx@lists.freedesktop.org,
	"Noralf Trønnes" <noralf@tronnes.org>,
	sam@ravnborg.org
Subject: [PATCH v8 4/5] drm/client: Hack: Add bootsplash example
Date: Sat,  8 Jun 2019 17:26:56 +0200	[thread overview]
Message-ID: <20190608152657.36613-5-noralf@tronnes.org> (raw)
In-Reply-To: <20190608152657.36613-1-noralf@tronnes.org>

An example to showcase the client API.

TODO:
- A bootsplash client needs a way to tell drm_fb_helper to stay away,
  otherwise it will chime in on setup and hotplug.
  Most DRM drivers register fbdev before calling drm_dev_register() (the
  generic emulation is an exception). This have to be reversed for
  bootsplash to fend off fbdev.
- Probably need some way to determine which is the primary display/device
  on multi DRM device systems.
- Maybe do handover from early/simple DRM driver

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/Kconfig          |   5 +
 drivers/gpu/drm/Makefile         |   1 +
 drivers/gpu/drm/drm_bootsplash.c | 358 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_client.c     |   7 +
 drivers/gpu/drm/drm_drv.c        |   4 +
 include/drm/drm_client.h         |   3 +
 6 files changed, 378 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_bootsplash.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 6b34949416b1..d77a67cf3a89 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -66,6 +66,11 @@ config DRM_DEBUG_SELFTEST
 
 	  If in doubt, say "N".
 
+config DRM_CLIENT_BOOTSPLASH
+	bool "DRM Bootsplash"
+	help
+	  DRM Bootsplash
+
 config DRM_KMS_HELPER
 	tristate
 	depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index d36feb4a6233..86a2ceb95838 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -31,6 +31,7 @@ drm-$(CONFIG_OF) += drm_of.o
 drm-$(CONFIG_AGP) += drm_agpsupport.o
 drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
 drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
+drm-$(CONFIG_DRM_CLIENT_BOOTSPLASH) += drm_bootsplash.o
 
 drm_vram_helper-y := drm_gem_vram_helper.o \
 		     drm_vram_helper_common.o \
diff --git a/drivers/gpu/drm/drm_bootsplash.c b/drivers/gpu/drm/drm_bootsplash.c
new file mode 100644
index 000000000000..f58ee19e268f
--- /dev/null
+++ b/drivers/gpu/drm/drm_bootsplash.c
@@ -0,0 +1,358 @@
+/* DRM internal client example */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/keyboard.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_client.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
+
+// drm_lastclose()
+#include "drm_internal.h"
+
+static bool drm_bootsplash_enabled = true;
+module_param_named(bootsplash_enabled, drm_bootsplash_enabled, bool, 0600);
+MODULE_PARM_DESC(bootsplash_enabled, "Enable bootsplash client [default=true]");
+
+struct drm_bootsplash {
+	struct drm_client_dev client;
+	struct mutex lock;
+	struct work_struct worker;
+	struct drm_client_buffer *buffers[2];
+	bool started;
+	bool stop;
+};
+
+static bool drm_bootsplash_key_pressed;
+
+static int drm_bootsplash_keyboard_notifier_call(struct notifier_block *blk,
+						 unsigned long code, void *_param)
+{
+	/* Any key is good */
+	drm_bootsplash_key_pressed = true;
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block drm_bootsplash_keyboard_notifier_block = {
+	.notifier_call = drm_bootsplash_keyboard_notifier_call,
+};
+
+static void drm_bootsplash_buffer_delete(struct drm_bootsplash *splash)
+{
+	unsigned int i;
+
+	for (i = 0; i < 2; i++) {
+		if (!IS_ERR_OR_NULL(splash->buffers[i]))
+			drm_client_framebuffer_delete(splash->buffers[i]);
+		splash->buffers[i] = NULL;
+	}
+}
+
+static int drm_bootsplash_buffer_create(struct drm_bootsplash *splash, u32 width, u32 height)
+{
+	unsigned int i;
+
+	for (i = 0; i < 2; i++) {
+		splash->buffers[i] = drm_client_framebuffer_create(&splash->client, width, height, DRM_FORMAT_XRGB8888);
+		if (IS_ERR(splash->buffers[i])) {
+			drm_bootsplash_buffer_delete(splash);
+			return PTR_ERR(splash->buffers[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int drm_bootsplash_display_probe(struct drm_bootsplash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+	unsigned int width = 0, height = 0;
+	unsigned int num_non_tiled = 0, i;
+	unsigned int modeset_mask = 0;
+	struct drm_mode_set *modeset;
+	bool tiled = false;
+	int ret;
+
+	ret = drm_client_modeset_probe(client, 0, 0);
+	if (ret)
+		return ret;
+
+	mutex_lock(&client->modeset_mutex);
+
+	drm_client_for_each_modeset(modeset, client) {
+		if (!modeset->mode)
+			continue;
+
+		if (modeset->connectors[0]->has_tile)
+			tiled = true;
+		else
+			num_non_tiled++;
+	}
+
+	if (!tiled && !num_non_tiled) {
+		drm_bootsplash_buffer_delete(splash);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	/* Assume only one tiled monitor is possible */
+	if (tiled) {
+		int hdisplay = 0, vdisplay = 0;
+
+		i = 0;
+		drm_client_for_each_modeset(modeset, client) {
+			i++;
+			if (!modeset->connectors[0]->has_tile)
+				continue;
+
+			if (!modeset->y)
+				hdisplay += modeset->mode->hdisplay;
+			if (!modeset->x)
+				vdisplay += modeset->mode->vdisplay;
+			modeset_mask |= BIT(i - 1);
+		}
+
+		width = hdisplay;
+		height = vdisplay;
+
+		goto trim;
+	}
+
+	/* The rest have one display (maybe cloned) per modeset, pick the largest */
+	i = 0;
+	drm_client_for_each_modeset(modeset, client) {
+		i++;
+		if (!modeset->mode || modeset->connectors[0]->has_tile)
+			continue;
+
+		if (modeset->mode->hdisplay * modeset->mode->vdisplay > width * height) {
+			width = modeset->mode->hdisplay;
+			height = modeset->mode->vdisplay;
+			modeset_mask = BIT(i - 1);
+		}
+	}
+
+trim:
+	/* Remove unused modesets (warning in __drm_atomic_helper_set_config()) */
+	i = 0;
+	drm_client_for_each_modeset(modeset, client) {
+		unsigned int j;
+
+		if (modeset_mask & BIT(i++))
+			continue;
+		drm_mode_destroy(client->dev, modeset->mode);
+		modeset->mode = NULL;
+
+		for (j = 0; j < modeset->num_connectors; j++) {
+			drm_connector_put(modeset->connectors[j]);
+			modeset->connectors[j] = NULL;
+		}
+		modeset->num_connectors = 0;
+	}
+
+	if (!splash->buffers[0] ||
+	    splash->buffers[0]->fb->width != width ||
+	    splash->buffers[0]->fb->height != height) {
+		drm_bootsplash_buffer_delete(splash);
+		ret = drm_bootsplash_buffer_create(splash, width, height);
+	}
+
+out:
+	mutex_unlock(&client->modeset_mutex);
+
+	return ret;
+}
+
+static int drm_bootsplash_display_commit_buffer(struct drm_bootsplash *splash, unsigned int num)
+{
+	struct drm_client_dev *client = &splash->client;
+	struct drm_mode_set *modeset;
+
+	mutex_lock(&client->modeset_mutex);
+	drm_client_for_each_modeset(modeset, client) {
+		if (modeset->mode)
+			modeset->fb = splash->buffers[num]->fb;
+	}
+	mutex_unlock(&client->modeset_mutex);
+
+	return drm_client_modeset_commit(client);
+}
+
+static u32 drm_bootsplash_color_table[3] = {
+	0x00ff0000, 0x0000ff00, 0x000000ff,
+};
+
+/* Draw a box with changing colors */
+static void drm_bootsplash_draw_box(struct drm_client_buffer *buffer, unsigned int sequence)
+{
+	unsigned int width = buffer->fb->width;
+	unsigned int height = buffer->fb->height;
+	unsigned int x, y;
+	u32 *pix;
+
+	pix = buffer->vaddr;
+	pix += ((height / 2) - 50) * width;
+	pix += (width / 2) - 50;
+
+	for (y = 0; y < 100; y++) {
+		for (x = 0; x < 100; x++)
+			*pix++ = drm_bootsplash_color_table[sequence];
+		pix += width - 100;
+	}
+}
+
+static int drm_bootsplash_draw(struct drm_bootsplash *splash, unsigned int sequence, unsigned int buffer_num)
+{
+	if (!splash->buffers[buffer_num])
+		return -ENOENT;
+
+	DRM_DEBUG_KMS("draw: buffer_num=%u, sequence=%u\n", buffer_num, sequence);
+
+	drm_bootsplash_draw_box(splash->buffers[buffer_num], sequence);
+
+	return drm_bootsplash_display_commit_buffer(splash, buffer_num);
+}
+
+static void drm_bootsplash_worker(struct work_struct *work)
+{
+	struct drm_bootsplash *splash = container_of(work, struct drm_bootsplash, worker);
+	struct drm_client_dev *client = &splash->client;
+	struct drm_device *dev = client->dev;
+	unsigned int buffer_num = 0, sequence = 0;
+	bool stop = false;
+	int ret = 0;
+
+	while (!drm_bootsplash_key_pressed) {
+		mutex_lock(&splash->lock);
+
+		stop = splash->stop;
+
+		buffer_num = !buffer_num;
+
+		ret = drm_bootsplash_draw(splash, sequence, buffer_num);
+
+		mutex_unlock(&splash->lock);
+
+		if (stop || ret == -ENOENT || ret == -EBUSY)
+			break;
+
+		if (++sequence == 3)
+			sequence = 0;
+
+		msleep(500);
+	}
+
+	/* Restore fbdev (or other) on key press. */
+	/* TODO: Check if it's OK to call drm_lastclose here. */
+	if (drm_bootsplash_key_pressed && !splash->stop)
+		drm_lastclose(dev);
+
+	drm_bootsplash_buffer_delete(splash);
+
+	DRM_DEV_DEBUG_KMS(dev->dev, "Bootsplash has stopped (key=%u stop=%u, ret=%d).\n",
+			  drm_bootsplash_key_pressed, splash->stop, ret);
+}
+
+static int drm_bootsplash_client_hotplug(struct drm_client_dev *client)
+{
+	struct drm_bootsplash *splash = container_of(client, struct drm_bootsplash, client);
+	int ret = 0;
+
+	if (drm_bootsplash_key_pressed)
+		return 0;
+
+	mutex_lock(&splash->lock);
+
+	if (splash->stop)
+		goto out_unlock;
+
+	ret = drm_bootsplash_display_probe(splash);
+	if (ret < 0) {
+		if (splash->started && ret == -ENOENT)
+			splash->stop = true;
+		goto out_unlock;
+	}
+
+	/*
+	 * TODO: Deal with rotated panels, might have to sw rotate
+	if (drm_client_panel_rotation(...))
+	 */
+
+	if (!splash->started) {
+		splash->started = true;
+		schedule_work(&splash->worker);
+	}
+
+out_unlock:
+	mutex_unlock(&splash->lock);
+
+	return ret;
+}
+
+static void drm_bootsplash_client_unregister(struct drm_client_dev *client)
+{
+	struct drm_bootsplash *splash = container_of(client, struct drm_bootsplash, client);
+
+	DRM_DEBUG_KMS("%s: IN\n", __func__);
+
+	mutex_lock(&splash->lock);
+	splash->stop = true;
+	mutex_unlock(&splash->lock);
+
+	flush_work(&splash->worker);
+
+	drm_client_release(client);
+	kfree(splash);
+
+	unregister_keyboard_notifier(&drm_bootsplash_keyboard_notifier_block);
+
+	DRM_DEBUG_KMS("%s: OUT\n", __func__);
+}
+
+static const struct drm_client_funcs drm_bootsplash_client_funcs = {
+	.owner		= THIS_MODULE,
+	.unregister	= drm_bootsplash_client_unregister,
+	.hotplug	= drm_bootsplash_client_hotplug,
+};
+
+void drm_bootsplash_client_register(struct drm_device *dev)
+{
+	struct drm_bootsplash *splash;
+	int ret;
+
+	if (!drm_bootsplash_enabled)
+		return;
+
+	splash = kzalloc(sizeof(*splash), GFP_KERNEL);
+	if (!splash)
+		return;
+
+	ret = drm_client_init(dev, &splash->client, "bootsplash", &drm_bootsplash_client_funcs);
+	if (ret) {
+		DRM_DEV_ERROR(dev->dev, "Failed to create client, ret=%d\n", ret);
+		kfree(splash);
+		return;
+	}
+
+	/* For this simple example only allow the first */
+	drm_bootsplash_enabled = false;
+
+	mutex_init(&splash->lock);
+
+	INIT_WORK(&splash->worker, drm_bootsplash_worker);
+
+	register_keyboard_notifier(&drm_bootsplash_keyboard_notifier_block);
+
+	drm_bootsplash_client_hotplug(&splash->client);
+
+	drm_client_register(&splash->client);
+}
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 410572f14257..2813b0cbdf39 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -161,6 +161,13 @@ void drm_client_release(struct drm_client_dev *client)
 }
 EXPORT_SYMBOL(drm_client_release);
 
+void drm_client_dev_register(struct drm_device *dev)
+{
+#if CONFIG_DRM_CLIENT_BOOTSPLASH
+	drm_bootsplash_client_register(dev);
+#endif
+}
+
 void drm_client_dev_unregister(struct drm_device *dev)
 {
 	struct drm_client_dev *client, *tmp;
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 95a55d98e152..754e34aed92e 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1017,6 +1017,10 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
 	drm_minor_unregister(dev, DRM_MINOR_RENDER);
 out_unlock:
 	mutex_unlock(&drm_global_mutex);
+
+	if (!ret)
+		drm_client_dev_register(dev);
+
 	return ret;
 }
 EXPORT_SYMBOL(drm_dev_register);
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index f2d5ed745733..e9eb770093fb 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -107,6 +107,7 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client,
 void drm_client_release(struct drm_client_dev *client);
 void drm_client_register(struct drm_client_dev *client);
 
+void drm_client_dev_register(struct drm_device *dev);
 void drm_client_dev_unregister(struct drm_device *dev);
 void drm_client_dev_hotplug(struct drm_device *dev);
 void drm_client_dev_restore(struct drm_device *dev);
@@ -183,4 +184,6 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode);
 
 int drm_client_debugfs_init(struct drm_minor *minor);
 
+void drm_bootsplash_client_register(struct drm_device *dev);
+
 #endif
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

  parent reply	other threads:[~2019-06-08 15:26 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-08 15:26 [PATCH v8 0/5] drm/fb-helper: Move modesetting code to drm_client Noralf Trønnes
2019-06-08 15:26 ` [PATCH v8 1/5] drm/fb-helper: Remove drm_fb_helper_connector Noralf Trønnes
2019-06-08 15:26 ` [PATCH v8 2/5] drm/fb-helper: Prepare to move out modeset config code Noralf Trønnes
2019-06-08 15:26 ` [PATCH v8 3/5] drm/fb-helper: Move " Noralf Trønnes
2019-06-08 15:26 ` Noralf Trønnes [this message]
2019-06-08 15:26 ` [PATCH v8 5/5] drm/todo: Add bootsplash entry Noralf Trønnes
2019-06-10 10:18 ` ✗ Fi.CI.CHECKPATCH: warning for drm/fb-helper: Move modesetting code to drm_client (rev9) Patchwork
2019-06-10 10:22 ` ✗ Fi.CI.SPARSE: " Patchwork
2019-06-10 10:41 ` ✓ Fi.CI.BAT: success " Patchwork
2019-06-10 23:14 ` ✓ Fi.CI.IGT: " Patchwork
2019-06-11 13:00 ` [PATCH v8 0/5] drm/fb-helper: Move modesetting code to drm_client Noralf Trønnes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190608152657.36613-5-noralf@tronnes.org \
    --to=noralf@tronnes.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=sam@ravnborg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.