| 1 | |
| 2 | |
| 3 | * Generic System Framebuffers
|
| 4 | * Copyright (c) 2012-2013 David Herrmann <[email protected]>
|
| 5 |
|
| 6 |
|
| 7 | |
| 8 | * Simple-Framebuffer support
|
| 9 | * Create a platform-device for any available boot framebuffer. The
|
| 10 | * simple-framebuffer platform device is already available on DT systems, so
|
| 11 | * this module parses the global "screen_info" object and creates a suitable
|
| 12 | * platform device compatible with the "simple-framebuffer" DT object. If
|
| 13 | * the framebuffer is incompatible, we instead create a legacy
|
| 14 | * "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and
|
| 15 | * pass the screen_info as platform_data. This allows legacy drivers
|
| 16 | * to pick these devices up without messing with simple-framebuffer drivers.
|
| 17 | * The global "screen_info" is still valid at all times.
|
| 18 | *
|
| 19 | * If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer"
|
| 20 | * platform devices, but only use legacy framebuffer devices for
|
| 21 | * backwards compatibility.
|
| 22 | *
|
| 23 | * TODO: We set the dev_id field of all platform-devices to 0. This allows
|
| 24 | * other OF/DT parsers to create such devices, too. However, they must
|
| 25 | * start at offset 1 for this to work.
|
| 26 |
|
| 27 |
|
| 28 | #include <linux/err.h>
|
| 29 | #include <linux/init.h>
|
| 30 | #include <linux/kernel.h>
|
| 31 | #include <linux/mm.h>
|
| 32 | #include <linux/pci.h>
|
| 33 | #include <linux/platform_data/simplefb.h>
|
| 34 | #include <linux/platform_device.h>
|
| 35 | #include <linux/screen_info.h>
|
| 36 | #include <linux/sysfb.h>
|
| 37 |
|
| 38 | static struct platform_device *pd;
|
| 39 | static DEFINE_MUTEX(disable_lock);
|
| 40 | static bool disabled;
|
| 41 |
|
| 42 | static struct device *sysfb_parent_dev(const struct screen_info *si);
|
| 43 |
|
| 44 | static bool sysfb_unregister(void)
|
| 45 | {
|
| 46 | if (IS_ERR_OR_NULL(pd))
|
| 47 | return false;
|
| 48 |
|
| 49 | platform_device_unregister(pd);
|
| 50 | pd = NULL;
|
| 51 |
|
| 52 | return true;
|
| 53 | }
|
| 54 |
|
| 55 | |
| 56 | * sysfb_disable() - disable the Generic System Framebuffers support
|
| 57 | * @dev: the device to check if non-NULL
|
| 58 | *
|
| 59 | * This disables the registration of system framebuffer devices that match the
|
| 60 | * generic drivers that make use of the system framebuffer set up by firmware.
|
| 61 | *
|
| 62 | * It also unregisters a device if this was already registered by sysfb_init().
|
| 63 | *
|
| 64 | * Context: The function can sleep. A @disable_lock mutex is acquired to serialize
|
| 65 | * against sysfb_init(), that registers a system framebuffer device.
|
| 66 |
|
| 67 | void sysfb_disable(struct device *dev)
|
| 68 | {
|
| 69 | struct screen_info *si = &sysfb_primary_display.screen;
|
| 70 | struct device *parent;
|
| 71 |
|
| 72 | mutex_lock(&disable_lock);
|
| 73 | parent = sysfb_parent_dev(si);
|
| 74 | if (!dev || !parent || dev == parent) {
|
| 75 | sysfb_unregister();
|
| 76 | disabled = true;
|
| 77 | }
|
| 78 | mutex_unlock(&disable_lock);
|
| 79 | }
|
| 80 | EXPORT_SYMBOL_GPL(sysfb_disable);
|
| 81 |
|
| 82 | |
| 83 | * sysfb_handles_screen_info() - reports if sysfb handles the global screen_info
|
| 84 | *
|
| 85 | * Callers can use sysfb_handles_screen_info() to determine whether the Generic
|
| 86 | * System Framebuffers (sysfb) can handle the global screen_info data structure
|
| 87 | * or not. Drivers might need this information to know if they have to setup the
|
| 88 | * system framebuffer, or if they have to delegate this action to sysfb instead.
|
| 89 | *
|
| 90 | * Returns:
|
| 91 | * True if sysfb handles the global screen_info data structure.
|
| 92 |
|
| 93 | bool sysfb_handles_screen_info(void)
|
| 94 | {
|
| 95 | const struct screen_info *si = &sysfb_primary_display.screen;
|
| 96 |
|
| 97 | return !!screen_info_video_type(si);
|
| 98 | }
|
| 99 | EXPORT_SYMBOL_GPL(sysfb_handles_screen_info);
|
| 100 |
|
| 101 | #if defined(CONFIG_PCI)
|
| 102 | static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
|
| 103 | {
|
| 104 | |
| 105 | * TODO: Try to integrate this code into the PCI subsystem
|
| 106 |
|
| 107 | int ret;
|
| 108 | u16 command;
|
| 109 |
|
| 110 | ret = pci_read_config_word(pdev, PCI_COMMAND, &command);
|
| 111 | if (ret != PCIBIOS_SUCCESSFUL)
|
| 112 | return false;
|
| 113 | if (!(command & PCI_COMMAND_MEMORY))
|
| 114 | return false;
|
| 115 | return true;
|
| 116 | }
|
| 117 | #else
|
| 118 | static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
|
| 119 | {
|
| 120 | return false;
|
| 121 | }
|
| 122 | #endif
|
| 123 |
|
| 124 | static struct device *sysfb_parent_dev(const struct screen_info *si)
|
| 125 | {
|
| 126 | struct pci_dev *pdev;
|
| 127 |
|
| 128 | pdev = screen_info_pci_dev(si);
|
| 129 | if (IS_ERR(pdev)) {
|
| 130 | return ERR_CAST(pdev);
|
| 131 | } else if (pdev) {
|
| 132 | if (!sysfb_pci_dev_is_enabled(pdev)) {
|
| 133 | pci_dev_put(pdev);
|
| 134 | return ERR_PTR(-ENODEV);
|
| 135 | }
|
| 136 | return &pdev->dev;
|
| 137 | }
|
| 138 |
|
| 139 | return NULL;
|
| 140 | }
|
| 141 |
|
| 142 | static __init int sysfb_init(void)
|
| 143 | {
|
| 144 | struct sysfb_display_info *dpy = &sysfb_primary_display;
|
| 145 | struct screen_info *si = &dpy->screen;
|
| 146 | struct device *parent;
|
| 147 | unsigned int type;
|
| 148 | struct simplefb_platform_data mode;
|
| 149 | const char *name;
|
| 150 | bool compatible;
|
| 151 | int ret = 0;
|
| 152 |
|
| 153 | screen_info_apply_fixups();
|
| 154 |
|
| 155 | mutex_lock(&disable_lock);
|
| 156 | if (disabled)
|
| 157 | goto unlock_mutex;
|
| 158 |
|
| 159 | sysfb_apply_efi_quirks(si);
|
| 160 |
|
| 161 | parent = sysfb_parent_dev(si);
|
| 162 | if (IS_ERR(parent)) {
|
| 163 | ret = PTR_ERR(parent);
|
| 164 | goto unlock_mutex;
|
| 165 | }
|
| 166 |
|
| 167 |
|
| 168 | compatible = sysfb_parse_mode(si, &mode);
|
| 169 | if (compatible) {
|
| 170 | pd = sysfb_create_simplefb(si, &mode, parent);
|
| 171 | if (!IS_ERR(pd))
|
| 172 | goto put_device;
|
| 173 | }
|
| 174 |
|
| 175 | type = screen_info_video_type(si);
|
| 176 |
|
| 177 |
|
| 178 | switch (type) {
|
| 179 | case VIDEO_TYPE_EGAC:
|
| 180 | name = "ega-framebuffer";
|
| 181 | break;
|
| 182 | case VIDEO_TYPE_VGAC:
|
| 183 | name = "vga-framebuffer";
|
| 184 | break;
|
| 185 | case VIDEO_TYPE_VLFB:
|
| 186 | name = "vesa-framebuffer";
|
| 187 | break;
|
| 188 | case VIDEO_TYPE_EFI:
|
| 189 | name = "efi-framebuffer";
|
| 190 | break;
|
| 191 | default:
|
| 192 | name = "platform-framebuffer";
|
| 193 | break;
|
| 194 | }
|
| 195 |
|
| 196 | pd = platform_device_alloc(name, 0);
|
| 197 | if (!pd) {
|
| 198 | ret = -ENOMEM;
|
| 199 | goto put_device;
|
| 200 | }
|
| 201 |
|
| 202 | pd->dev.parent = parent;
|
| 203 |
|
| 204 | sysfb_set_efifb_fwnode(si, pd);
|
| 205 |
|
| 206 | ret = platform_device_add_data(pd, dpy, sizeof(*dpy));
|
| 207 | if (ret)
|
| 208 | goto err;
|
| 209 |
|
| 210 | ret = platform_device_add(pd);
|
| 211 | if (ret)
|
| 212 | goto err;
|
| 213 |
|
| 214 | goto put_device;
|
| 215 | err:
|
| 216 | platform_device_put(pd);
|
| 217 | put_device:
|
| 218 | put_device(parent);
|
| 219 | unlock_mutex:
|
| 220 | mutex_unlock(&disable_lock);
|
| 221 | return ret;
|
| 222 | }
|
| 223 |
|
| 224 |
|
| 225 | device_initcall(sysfb_init);
|