162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic System Framebuffers
462306a36Sopenharmony_ci * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * Simple-Framebuffer support
962306a36Sopenharmony_ci * Create a platform-device for any available boot framebuffer. The
1062306a36Sopenharmony_ci * simple-framebuffer platform device is already available on DT systems, so
1162306a36Sopenharmony_ci * this module parses the global "screen_info" object and creates a suitable
1262306a36Sopenharmony_ci * platform device compatible with the "simple-framebuffer" DT object. If
1362306a36Sopenharmony_ci * the framebuffer is incompatible, we instead create a legacy
1462306a36Sopenharmony_ci * "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and
1562306a36Sopenharmony_ci * pass the screen_info as platform_data. This allows legacy drivers
1662306a36Sopenharmony_ci * to pick these devices up without messing with simple-framebuffer drivers.
1762306a36Sopenharmony_ci * The global "screen_info" is still valid at all times.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer"
2062306a36Sopenharmony_ci * platform devices, but only use legacy framebuffer devices for
2162306a36Sopenharmony_ci * backwards compatibility.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * TODO: We set the dev_id field of all platform-devices to 0. This allows
2462306a36Sopenharmony_ci * other OF/DT parsers to create such devices, too. However, they must
2562306a36Sopenharmony_ci * start at offset 1 for this to work.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/err.h>
2962306a36Sopenharmony_ci#include <linux/init.h>
3062306a36Sopenharmony_ci#include <linux/kernel.h>
3162306a36Sopenharmony_ci#include <linux/mm.h>
3262306a36Sopenharmony_ci#include <linux/platform_data/simplefb.h>
3362306a36Sopenharmony_ci#include <linux/platform_device.h>
3462306a36Sopenharmony_ci#include <linux/screen_info.h>
3562306a36Sopenharmony_ci#include <linux/sysfb.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct platform_device *pd;
3862306a36Sopenharmony_cistatic DEFINE_MUTEX(disable_lock);
3962306a36Sopenharmony_cistatic bool disabled;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic bool sysfb_unregister(void)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(pd))
4462306a36Sopenharmony_ci		return false;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	platform_device_unregister(pd);
4762306a36Sopenharmony_ci	pd = NULL;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return true;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/**
5362306a36Sopenharmony_ci * sysfb_disable() - disable the Generic System Framebuffers support
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * This disables the registration of system framebuffer devices that match the
5662306a36Sopenharmony_ci * generic drivers that make use of the system framebuffer set up by firmware.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * It also unregisters a device if this was already registered by sysfb_init().
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * Context: The function can sleep. A @disable_lock mutex is acquired to serialize
6162306a36Sopenharmony_ci *          against sysfb_init(), that registers a system framebuffer device.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_civoid sysfb_disable(void)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	mutex_lock(&disable_lock);
6662306a36Sopenharmony_ci	sysfb_unregister();
6762306a36Sopenharmony_ci	disabled = true;
6862306a36Sopenharmony_ci	mutex_unlock(&disable_lock);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfb_disable);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic __init int sysfb_init(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct screen_info *si = &screen_info;
7562306a36Sopenharmony_ci	struct simplefb_platform_data mode;
7662306a36Sopenharmony_ci	const char *name;
7762306a36Sopenharmony_ci	bool compatible;
7862306a36Sopenharmony_ci	int ret = 0;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	mutex_lock(&disable_lock);
8162306a36Sopenharmony_ci	if (disabled)
8262306a36Sopenharmony_ci		goto unlock_mutex;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	sysfb_apply_efi_quirks();
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* try to create a simple-framebuffer device */
8762306a36Sopenharmony_ci	compatible = sysfb_parse_mode(si, &mode);
8862306a36Sopenharmony_ci	if (compatible) {
8962306a36Sopenharmony_ci		pd = sysfb_create_simplefb(si, &mode);
9062306a36Sopenharmony_ci		if (!IS_ERR(pd))
9162306a36Sopenharmony_ci			goto unlock_mutex;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* if the FB is incompatible, create a legacy framebuffer device */
9562306a36Sopenharmony_ci	if (si->orig_video_isVGA == VIDEO_TYPE_EFI)
9662306a36Sopenharmony_ci		name = "efi-framebuffer";
9762306a36Sopenharmony_ci	else if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
9862306a36Sopenharmony_ci		name = "vesa-framebuffer";
9962306a36Sopenharmony_ci	else if (si->orig_video_isVGA == VIDEO_TYPE_VGAC)
10062306a36Sopenharmony_ci		name = "vga-framebuffer";
10162306a36Sopenharmony_ci	else if (si->orig_video_isVGA == VIDEO_TYPE_EGAC)
10262306a36Sopenharmony_ci		name = "ega-framebuffer";
10362306a36Sopenharmony_ci	else
10462306a36Sopenharmony_ci		name = "platform-framebuffer";
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	pd = platform_device_alloc(name, 0);
10762306a36Sopenharmony_ci	if (!pd) {
10862306a36Sopenharmony_ci		ret = -ENOMEM;
10962306a36Sopenharmony_ci		goto unlock_mutex;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	sysfb_set_efifb_fwnode(pd);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ret = platform_device_add_data(pd, si, sizeof(*si));
11562306a36Sopenharmony_ci	if (ret)
11662306a36Sopenharmony_ci		goto err;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ret = platform_device_add(pd);
11962306a36Sopenharmony_ci	if (ret)
12062306a36Sopenharmony_ci		goto err;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	goto unlock_mutex;
12362306a36Sopenharmony_cierr:
12462306a36Sopenharmony_ci	platform_device_put(pd);
12562306a36Sopenharmony_ciunlock_mutex:
12662306a36Sopenharmony_ci	mutex_unlock(&disable_lock);
12762306a36Sopenharmony_ci	return ret;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* must execute after PCI subsystem for EFI quirks */
13162306a36Sopenharmony_cidevice_initcall(sysfb_init);
132