1 // SPDX-License-Identifier: MIT
2
3 #include <drm/drm_client.h>
4 #include <drm/drm_crtc_helper.h>
5 #include <drm/drm_drv.h>
6 #include <drm/drm_fb_helper.h>
7 #include <drm/drm_fourcc.h>
8 #include <drm/drm_print.h>
9
10 #include "drm_client_internal.h"
11
12 /*
13 * struct drm_client_funcs
14 */
15
drm_fbdev_client_free(struct drm_client_dev * client)16 static void drm_fbdev_client_free(struct drm_client_dev *client)
17 {
18 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
19
20 drm_fb_helper_unprepare(fb_helper);
21 kfree(fb_helper);
22 }
23
drm_fbdev_client_unregister(struct drm_client_dev * client)24 static void drm_fbdev_client_unregister(struct drm_client_dev *client)
25 {
26 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
27
28 if (fb_helper->info) {
29 /*
30 * Fully probed framebuffer device
31 */
32 drm_fb_helper_unregister_info(fb_helper);
33 } else {
34 /*
35 * Partially initialized client, no framebuffer device yet
36 */
37 drm_client_release(&fb_helper->client);
38 }
39 }
40
drm_fbdev_client_restore(struct drm_client_dev * client,bool force)41 static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force)
42 {
43 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
44
45 /*
46 * The client is registered before the initial fbdev probe.
47 * If probing failed, the client remains registered but there
48 * is no valid fbdev framebuffer to restore.
49 */
50 if (!fb_helper->info || !fb_helper->fb)
51 return 0;
52
53 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
54
55 return 0;
56 }
57
drm_fbdev_client_hotplug(struct drm_client_dev * client)58 static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
59 {
60 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
61 struct drm_device *dev = client->dev;
62 int ret;
63
64 if (dev->fb_helper)
65 return drm_fb_helper_hotplug_event(dev->fb_helper);
66
67 ret = drm_fb_helper_init(dev, fb_helper);
68 if (ret)
69 goto err_drm_err;
70
71 if (!drm_drv_uses_atomic_modeset(dev))
72 drm_helper_disable_unused_functions(dev);
73
74 ret = drm_fb_helper_initial_config(fb_helper);
75 if (ret)
76 goto err_drm_fb_helper_fini;
77
78 return 0;
79
80 err_drm_fb_helper_fini:
81 drm_fb_helper_fini(fb_helper);
82 err_drm_err:
83 drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
84 return ret;
85 }
86
drm_fbdev_client_suspend(struct drm_client_dev * client)87 static int drm_fbdev_client_suspend(struct drm_client_dev *client)
88 {
89 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
90
91 drm_fb_helper_set_suspend_unlocked(fb_helper, true);
92
93 return 0;
94 }
95
drm_fbdev_client_resume(struct drm_client_dev * client)96 static int drm_fbdev_client_resume(struct drm_client_dev *client)
97 {
98 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
99
100 drm_fb_helper_set_suspend_unlocked(fb_helper, false);
101
102 return 0;
103 }
104
105 static const struct drm_client_funcs drm_fbdev_client_funcs = {
106 .owner = THIS_MODULE,
107 .free = drm_fbdev_client_free,
108 .unregister = drm_fbdev_client_unregister,
109 .restore = drm_fbdev_client_restore,
110 .hotplug = drm_fbdev_client_hotplug,
111 .suspend = drm_fbdev_client_suspend,
112 .resume = drm_fbdev_client_resume,
113 };
114
115 /**
116 * drm_fbdev_client_setup() - Setup fbdev emulation
117 * @dev: DRM device
118 * @format: Preferred color format for the device. DRM_FORMAT_XRGB8888
119 * is used if this is zero.
120 *
121 * This function sets up fbdev emulation. Restore, hotplug events and
122 * teardown are all taken care of. Drivers that do suspend/resume need
123 * to call drm_client_dev_suspend() and drm_client_dev_resume() by
124 * themselves. Simple drivers might use drm_mode_config_helper_suspend().
125 *
126 * This function is safe to call even when there are no connectors present.
127 * Setup will be retried on the next hotplug event.
128 *
129 * The fbdev client is destroyed by drm_dev_unregister().
130 *
131 * Returns:
132 * 0 on success, or a negative errno code otherwise.
133 */
drm_fbdev_client_setup(struct drm_device * dev,const struct drm_format_info * format)134 int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format)
135 {
136 struct drm_fb_helper *fb_helper;
137 unsigned int color_mode;
138 int ret;
139
140 /* TODO: Use format info throughout DRM */
141 if (format) {
142 unsigned int bpp = drm_format_info_bpp(format, 0);
143
144 switch (bpp) {
145 case 16:
146 color_mode = format->depth; // could also be 15
147 break;
148 default:
149 color_mode = bpp;
150 }
151 } else {
152 switch (dev->mode_config.preferred_depth) {
153 case 0:
154 case 24:
155 color_mode = 32;
156 break;
157 default:
158 color_mode = dev->mode_config.preferred_depth;
159 }
160 }
161
162 drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
163 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
164
165 fb_helper = kzalloc_obj(*fb_helper);
166 if (!fb_helper)
167 return -ENOMEM;
168 drm_fb_helper_prepare(dev, fb_helper, color_mode, NULL);
169
170 ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
171 if (ret) {
172 drm_err(dev, "Failed to register client: %d\n", ret);
173 goto err_drm_client_init;
174 }
175
176 drm_client_register(&fb_helper->client);
177
178 return 0;
179
180 err_drm_client_init:
181 drm_fb_helper_unprepare(fb_helper);
182 kfree(fb_helper);
183 return ret;
184 }
185