Merge tag 'nfs-for-6.12-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
[linux-2.6-block.git] / drivers / firmware / sysfb.c
CommitLineData
2874c5fd 1// SPDX-License-Identifier: GPL-2.0-or-later
e3263ab3 2/*
8633ef82 3 * Generic System Framebuffers
e3263ab3 4 * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
e3263ab3
DH
5 */
6
7/*
8633ef82 8 * Simple-Framebuffer support
e3263ab3
DH
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 *
8633ef82 19 * If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer"
e3263ab3
DH
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
8633ef82 24 * other OF/DT parsers to create such devices, too. However, they must
e3263ab3
DH
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>
9eac534d 32#include <linux/pci.h>
e3263ab3
DH
33#include <linux/platform_data/simplefb.h>
34#include <linux/platform_device.h>
35#include <linux/screen_info.h>
d391c582 36#include <linux/sysfb.h>
e3263ab3 37
bc824922
JMC
38static struct platform_device *pd;
39static DEFINE_MUTEX(disable_lock);
40static bool disabled;
41
b49420d6
AD
42static struct device *sysfb_parent_dev(const struct screen_info *si);
43
bc824922
JMC
44static 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
b49420d6 57 * @dev: the device to check if non-NULL
bc824922
JMC
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 */
b49420d6 67void sysfb_disable(struct device *dev)
bc824922 68{
b49420d6
AD
69 struct screen_info *si = &screen_info;
70
bc824922 71 mutex_lock(&disable_lock);
b49420d6
AD
72 if (!dev || dev == sysfb_parent_dev(si)) {
73 sysfb_unregister();
74 disabled = true;
75 }
bc824922
JMC
76 mutex_unlock(&disable_lock);
77}
78EXPORT_SYMBOL_GPL(sysfb_disable);
79
4e754597 80#if defined(CONFIG_PCI)
b49420d6 81static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
4e754597
TZ
82{
83 /*
84 * TODO: Try to integrate this code into the PCI subsystem
85 */
86 int ret;
87 u16 command;
88
89 ret = pci_read_config_word(pdev, PCI_COMMAND, &command);
90 if (ret != PCIBIOS_SUCCESSFUL)
91 return false;
92 if (!(command & PCI_COMMAND_MEMORY))
93 return false;
94 return true;
95}
96#else
b49420d6 97static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
4e754597
TZ
98{
99 return false;
100}
101#endif
102
b49420d6 103static struct device *sysfb_parent_dev(const struct screen_info *si)
9eac534d
TZ
104{
105 struct pci_dev *pdev;
106
107 pdev = screen_info_pci_dev(si);
4e754597 108 if (IS_ERR(pdev)) {
9eac534d 109 return ERR_CAST(pdev);
4e754597 110 } else if (pdev) {
3285d8f0
TZ
111 if (!sysfb_pci_dev_is_enabled(pdev)) {
112 pci_dev_put(pdev);
4e754597 113 return ERR_PTR(-ENODEV);
3285d8f0 114 }
9eac534d 115 return &pdev->dev;
4e754597 116 }
9eac534d
TZ
117
118 return NULL;
119}
120
e3263ab3
DH
121static __init int sysfb_init(void)
122{
123 struct screen_info *si = &screen_info;
9eac534d 124 struct device *parent;
e3263ab3 125 struct simplefb_platform_data mode;
e3263ab3
DH
126 const char *name;
127 bool compatible;
bc824922
JMC
128 int ret = 0;
129
78aa89d1
TZ
130 screen_info_apply_fixups();
131
bc824922
JMC
132 mutex_lock(&disable_lock);
133 if (disabled)
134 goto unlock_mutex;
e3263ab3 135
3615c786
HG
136 sysfb_apply_efi_quirks();
137
9eac534d 138 parent = sysfb_parent_dev(si);
9fa2679b
DC
139 if (IS_ERR(parent)) {
140 ret = PTR_ERR(parent);
4e754597 141 goto unlock_mutex;
9fa2679b 142 }
9eac534d 143
e3263ab3 144 /* try to create a simple-framebuffer device */
8633ef82 145 compatible = sysfb_parse_mode(si, &mode);
e3263ab3 146 if (compatible) {
9eac534d 147 pd = sysfb_create_simplefb(si, &mode, parent);
0949ee75 148 if (!IS_ERR(pd))
3285d8f0 149 goto put_device;
e3263ab3
DH
150 }
151
152 /* if the FB is incompatible, create a legacy framebuffer device */
153 if (si->orig_video_isVGA == VIDEO_TYPE_EFI)
154 name = "efi-framebuffer";
155 else if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
156 name = "vesa-framebuffer";
0db5b61e
TZ
157 else if (si->orig_video_isVGA == VIDEO_TYPE_VGAC)
158 name = "vga-framebuffer";
159 else if (si->orig_video_isVGA == VIDEO_TYPE_EGAC)
160 name = "ega-framebuffer";
e3263ab3
DH
161 else
162 name = "platform-framebuffer";
163
8633ef82 164 pd = platform_device_alloc(name, 0);
bc824922
JMC
165 if (!pd) {
166 ret = -ENOMEM;
3285d8f0 167 goto put_device;
bc824922 168 }
8633ef82 169
9eac534d
TZ
170 pd->dev.parent = parent;
171
3615c786 172 sysfb_set_efifb_fwnode(pd);
8633ef82
JMC
173
174 ret = platform_device_add_data(pd, si, sizeof(*si));
175 if (ret)
176 goto err;
177
178 ret = platform_device_add(pd);
179 if (ret)
180 goto err;
181
3285d8f0 182 goto put_device;
8633ef82
JMC
183err:
184 platform_device_put(pd);
3285d8f0
TZ
185put_device:
186 put_device(parent);
bc824922
JMC
187unlock_mutex:
188 mutex_unlock(&disable_lock);
8633ef82 189 return ret;
e3263ab3
DH
190}
191
2995e506 192/* must execute after PCI subsystem for EFI quirks */
d1b163aa 193device_initcall(sysfb_init);