Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a591cf60 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/client: Hack: Add bootsplash example"

parents 367fdfc5 31f1535f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -35,6 +35,13 @@ config DRM_DP_AUX_CHARDEV
	  read and write values to arbitrary DPCD registers on the DP aux
	  channel.

config DRM_CLIENT_BOOTSPLASH
	bool "DRM Bootsplash"
	help
	  Choose this option to enable DRM bootsplash. This option needs to be
	  selected only if UEFI bootsplash is disabled. Choosing this option
	  will render splash logo in display panel during boot up.

config DRM_KMS_HELPER
	tristate
	depends on DRM
+1 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ drm-$(CONFIG_PCI) += ati_pcigart.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
drm-$(CONFIG_OF) += drm_of.o
drm-$(CONFIG_AGP) += drm_agpsupport.o
drm-$(CONFIG_DRM_CLIENT_BOOTSPLASH) += drm_bootsplash.o

drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
		drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
+348 −0
Original line number Diff line number Diff line
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/notifier.h>
#include <linux/keyboard.h>
#include <linux/completion.h>

#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_modes.h>
#include <drm/drm_client.h>

#include "drm_internal.h"
#include "drm_splash.h"

static bool drm_bootsplash_enabled = true;
module_param_named(bootsplash_enabled, drm_bootsplash_enabled, bool, 0664);

static void drm_bootsplash_client_unregister(struct drm_client_dev *client);
static int drm_bootsplash_client_hotplug(struct drm_client_dev *client);

struct drm_bootsplash {
	struct drm_client_dev client;
	struct mutex lock;
	struct drm_client_display *display;
	struct drm_client_buffer *buffer[2];
	struct work_struct worker;
	struct completion xref;
	bool started;
	bool stop;
};

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->buffer[i]))
			drm_client_framebuffer_delete(splash->buffer[i]);
		splash->buffer[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->buffer[i] =
			drm_client_framebuffer_create(&splash->client,
					width, height, SPLASH_IMAGE_FORMAT);
		if (IS_ERR(splash->buffer[i])) {
			drm_bootsplash_buffer_delete(splash);
			return PTR_ERR(splash->buffer[i]);
		}

		splash->buffer[i]->vaddr =
			drm_client_buffer_vmap(splash->buffer[i]);
		if (!(splash->buffer[i]->vaddr))
			DRM_ERROR("drm_client_buffer_vmap fail\n");

	}

	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 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:
	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_unreference(modeset->connectors[j]);
			modeset->connectors[j] = NULL;
		}
		modeset->num_connectors = 0;
	}

	if (!splash->buffer[0] ||
	    splash->buffer[0]->fb->width != width ||
	    splash->buffer[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->buffer[num]->fb;
	}
	mutex_unlock(&client->modeset_mutex);

	return drm_client_modeset_commit(client);
}

/* Draw a box for copying the image */
static void drm_bootsplash_draw_box(struct drm_client_buffer *buffer)
{
	unsigned int width = buffer->fb->width;
	unsigned int height = buffer->fb->height;
	unsigned int x, y, z;
	u32 *pix;

	pix = buffer->vaddr;
	pix += ((height / 2) - 50) * width;
	pix += (width / 2) - 50;

	z = 0;
	for (y = 0; y < SPLASH_IMAGE_HEIGHT; y++) {
		for (x = 0; x < SPLASH_IMAGE_WIDTH; x++)
			*pix++ = splash_bgr888_image[z++];
		pix += width - SPLASH_IMAGE_WIDTH;
	}
}

static int drm_bootsplash_draw(struct drm_bootsplash *splash,
							unsigned int buffer_num)
{
	if (!splash->buffer[buffer_num])
		return -ENOENT;

	drm_bootsplash_draw_box(splash->buffer[buffer_num]);

	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;
	bool stop = false;
	int ret = 0, times = 0;

	while (!splash->stop) {
		mutex_lock(&splash->lock);

		stop = splash->stop;

		buffer_num = !buffer_num;

		ret = drm_bootsplash_draw(splash, buffer_num);

		mutex_unlock(&splash->lock);

		if (stop || ret == -ENOENT || ret == -EBUSY)
			break;

		if (times == 10)
			splash->stop = true;
		else
			times++;

		msleep(500);
	}

	if ((times == 10) && splash->stop)
		drm_lastclose(dev);

	drm_bootsplash_buffer_delete(splash);

	DRM_DEBUG("Bootsplash has stopped (start=%u, stop=%u, ret=%d).\n",
			splash->started, splash->stop, ret);

	complete(&splash->xref);
}

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, retval;

	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;
	}

	if (!splash->started) {
		splash->started = true;
		reinit_completion(&splash->xref);
		schedule_work(&splash->worker);
		retval = wait_for_completion_interruptible(&splash->xref);
		if (retval < 0)
			DRM_ERROR("wait for bootsplash worker failed\n");
	}

out_unlock:
	return ret;
}

static const struct drm_client_funcs drm_bootsplash_client_funcs = {
	.owner          = THIS_MODULE,
	.unregister     = drm_bootsplash_client_unregister,
	.hotplug        = drm_bootsplash_client_hotplug,
};

static void drm_bootsplash_client_unregister(struct drm_client_dev *client)
{
	struct drm_bootsplash *splash =
		container_of(client, struct drm_bootsplash, client);

	mutex_lock(&splash->lock);
	splash->stop = true;
	mutex_unlock(&splash->lock);

	flush_work(&splash->worker);

	drm_client_release(client);
	kfree(splash);
}

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, "Fail 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);
	init_completion(&splash->xref);

	ret = drm_bootsplash_client_hotplug(&splash->client);
	if (ret)
		DRM_DEV_ERROR(dev->dev, "client hotplug ret=%d\n", ret);

	drm_client_register(&splash->client);
}

MODULE_DESCRIPTION("bootsplash");
+64 −12
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ static void drm_client_close(struct drm_client_dev *client)

	drm_file_free(client->file);
}
EXPORT_SYMBOL(drm_client_close);

/**
 * drm_client_init - Initialise a DRM client
@@ -157,6 +156,13 @@ void drm_client_release(struct drm_client_dev *client)
}
EXPORT_SYMBOL(drm_client_release);

void drm_client_dev_register(struct drm_device *dev)
{
#ifdef 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;
@@ -176,6 +182,7 @@ void drm_client_dev_unregister(struct drm_device *dev)
	}
	mutex_unlock(&dev->clientlist_mutex);
}
EXPORT_SYMBOL(drm_client_dev_unregister);

/**
 * drm_client_dev_hotplug - Send hotplug event to clients
@@ -249,7 +256,6 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
	struct drm_device *dev = client->dev;
	struct drm_client_buffer *buffer;
	struct drm_gem_object *obj;
	void *vaddr;
	int ret;

	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
@@ -276,6 +282,40 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u

	buffer->gem = obj;

	return buffer;

err_delete:
	drm_client_buffer_delete(buffer);

	return ERR_PTR(ret);
}

/**
 * drm_client_buffer_vmap - Map DRM client buffer into address space
 * @buffer: DRM client buffer
 *
 * This function maps a client buffer into kernel address space. If the
 * buffer is already mapped, it returns the mapping's address.
 *
 * Client buffer mappings are not ref'counted. Each call to
 * drm_client_buffer_vmap() should be followed by a call to
 * drm_client_buffer_vunmap(); or the client buffer should be mapped
 * throughout its lifetime. The latter is the default.
 *
 * Returns:
 *	The mapped memory's address
 */
void *drm_client_buffer_vmap(struct drm_client_buffer *buffer)
{
	struct drm_device *dev = buffer->client->dev;
	void *vaddr;

	if (buffer->vaddr)
		return buffer->vaddr;

	if (!dev->driver->gem_prime_vmap)
		return ERR_PTR(-ENOTSUPP);

	/*
	 * FIXME: The dependency on GEM here isn't required, we could
	 * convert the driver handle to a dma-buf instead and use the
@@ -284,21 +324,33 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
	 * fd_install step out of the driver backend hooks, to make that
	 * final step optional for internal users.
	 */
	vaddr = dev->driver->gem_prime_vmap(obj);
	if (!vaddr) {
		ret = -ENOMEM;
		goto err_delete;
	}
	vaddr = dev->driver->gem_prime_vmap(buffer->gem);
	if (IS_ERR(vaddr))
		return vaddr;

	buffer->vaddr = vaddr;

	return buffer;

err_delete:
	drm_client_buffer_delete(buffer);
	return vaddr;
}
EXPORT_SYMBOL(drm_client_buffer_vmap);
/**
 * drm_client_buffer_vunmap - Unmap DRM client buffer
 * @buffer: DRM client buffer
 *
 * This function removes a client buffer's memory mapping. This
 * function is only required by clients that manage their buffers
 * by themselves. By default, DRM client buffers are mapped throughout
 * their entire lifetime.
 */
void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
{
	struct drm_device *dev = buffer->client->dev;

	return ERR_PTR(ret);
	if (buffer->vaddr && dev->driver->gem_prime_vunmap)
		dev->driver->gem_prime_vunmap(buffer->gem, buffer->vaddr);
	buffer->vaddr = NULL;
}
EXPORT_SYMBOL(drm_client_buffer_vunmap);

static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
{
+7 −11
Original line number Diff line number Diff line
@@ -1050,8 +1050,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
		goto out_unlock;
	}

	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
		if (connector->encoder_ids[i] != 0)
	drm_connector_for_each_possible_encoder(connector, encoder, i)
		encoders_count++;

	if (out_resp->count_modes == 0) {
@@ -1112,17 +1111,14 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
	if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
		copied = 0;
		encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
			if (connector->encoder_ids[i] != 0) {
				if (put_user(connector->encoder_ids[i],
					     encoder_ptr + copied)) {
		drm_connector_for_each_possible_encoder(connector, encoder, i) {
			if (put_user(encoder->base.id, encoder_ptr + copied)) {
				ret = -EFAULT;
				goto out;
			}
			copied++;
		}
	}
	}
	out_resp->count_encoders = encoders_count;

out:
Loading