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