Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226// SPDX-License-Identifier: GPL-2.0-or-later/* * Generic System Framebuffers * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> */
/* * Simple-Framebuffer support * Create a platform-device for any available boot framebuffer. The * simple-framebuffer platform device is already available on DT systems, so * this module parses the global "screen_info" object and creates a suitable * platform device compatible with the "simple-framebuffer" DT object. If * the framebuffer is incompatible, we instead create a legacy * "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and * pass the screen_info as platform_data. This allows legacy drivers * to pick these devices up without messing with simple-framebuffer drivers. * The global "screen_info" is still valid at all times. * * If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer" * platform devices, but only use legacy framebuffer devices for * backwards compatibility. * * TODO: We set the dev_id field of all platform-devices to 0. This allows * other OF/DT parsers to create such devices, too. However, they must * start at offset 1 for this to work. */
#include <linux/err.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/platform_data/simplefb.h>#include <linux/platform_device.h>#include <linux/screen_info.h>#include <linux/sysfb.h>
static struct platform_device *pd;static DEFINE_MUTEX(disable_lock);static bool disabled;
static struct device *sysfb_parent_dev(const struct screen_info *si);
static bool sysfb_unregister(void){ if (IS_ERR_OR_NULL(pd)) return false;
platform_device_unregister(pd); pd = NULL;
return true;}
/** * sysfb_disable() - disable the Generic System Framebuffers support * @dev: the device to check if non-NULL * * This disables the registration of system framebuffer devices that match the * generic drivers that make use of the system framebuffer set up by firmware. * * It also unregisters a device if this was already registered by sysfb_init(). * * Context: The function can sleep. A @disable_lock mutex is acquired to serialize * against sysfb_init(), that registers a system framebuffer device. */void sysfb_disable(struct device *dev){ struct screen_info *si = &sysfb_primary_display.screen; struct device *parent;
mutex_lock(&disable_lock); parent = sysfb_parent_dev(si); if (!dev || !parent || dev == parent) { sysfb_unregister(); disabled = true; } mutex_unlock(&disable_lock);}EXPORT_SYMBOL_GPL(sysfb_disable);
/** * sysfb_handles_screen_info() - reports if sysfb handles the global screen_info * * Callers can use sysfb_handles_screen_info() to determine whether the Generic * System Framebuffers (sysfb) can handle the global screen_info data structure * or not. Drivers might need this information to know if they have to setup the * system framebuffer, or if they have to delegate this action to sysfb instead. * * Returns: * True if sysfb handles the global screen_info data structure. */bool sysfb_handles_screen_info(void){ const struct screen_info *si = &sysfb_primary_display.screen;
return !!screen_info_video_type(si);}EXPORT_SYMBOL_GPL(sysfb_handles_screen_info);
#if defined(CONFIG_PCI)static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev){ /* * TODO: Try to integrate this code into the PCI subsystem */ int ret; u16 command;
ret = pci_read_config_word(pdev, PCI_COMMAND, &command); if (ret != PCIBIOS_SUCCESSFUL) return false; if (!(command & PCI_COMMAND_MEMORY)) return false; return true;}#elsestatic bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev){ return false;}#endif
static struct device *sysfb_parent_dev(const struct screen_info *si){ struct pci_dev *pdev;
pdev = screen_info_pci_dev(si); if (IS_ERR(pdev)) { return ERR_CAST(pdev); } else if (pdev) { if (!sysfb_pci_dev_is_enabled(pdev)) { pci_dev_put(pdev); return ERR_PTR(-ENODEV); } return &pdev->dev; }
return NULL;}
static __init int sysfb_init(void){ struct sysfb_display_info *dpy = &sysfb_primary_display; struct screen_info *si = &dpy->screen; struct device *parent; unsigned int type; struct simplefb_platform_data mode; const char *name; bool compatible; int ret = 0;
screen_info_apply_fixups();
mutex_lock(&disable_lock); if (disabled) goto unlock_mutex;
sysfb_apply_efi_quirks(si);
parent = sysfb_parent_dev(si); if (IS_ERR(parent)) { ret = PTR_ERR(parent); goto unlock_mutex; }
/* try to create a simple-framebuffer device */ compatible = sysfb_parse_mode(si, &mode); if (compatible) { pd = sysfb_create_simplefb(si, &mode, parent); if (!IS_ERR(pd)) goto put_device; }
type = screen_info_video_type(si);
/* if the FB is incompatible, create a legacy framebuffer device */ switch (type) { case VIDEO_TYPE_EGAC: name = "ega-framebuffer"; break; case VIDEO_TYPE_VGAC: name = "vga-framebuffer"; break; case VIDEO_TYPE_VLFB: name = "vesa-framebuffer"; break; case VIDEO_TYPE_EFI: name = "efi-framebuffer"; break; default: name = "platform-framebuffer"; break; }
pd = platform_device_alloc(name, 0); if (!pd) { ret = -ENOMEM; goto put_device; }
pd->dev.parent = parent;
sysfb_set_efifb_fwnode(si, pd);
ret = platform_device_add_data(pd, dpy, sizeof(*dpy)); if (ret) goto err;
ret = platform_device_add(pd); if (ret) goto err;
goto put_device;err: platform_device_put(pd);put_device: put_device(parent);unlock_mutex: mutex_unlock(&disable_lock); return ret;}
/* must execute after PCI subsystem for EFI quirks */device_initcall(sysfb_init);