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 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
46
47 return 0;
48 }
49
drm_fbdev_client_hotplug(struct drm_client_dev * client)50 static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
51 {
52 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
53 struct drm_device *dev = client->dev;
54 int ret;
55
56 if (dev->fb_helper)
57 return drm_fb_helper_hotplug_event(dev->fb_helper);
58
59 ret = drm_fb_helper_init(dev, fb_helper);
60 if (ret)
61 goto err_drm_err;
62
63 if (!drm_drv_uses_atomic_modeset(dev))
64 drm_helper_disable_unused_functions(dev);
65
66 ret = drm_fb_helper_initial_config(fb_helper);
67 if (ret)
68 goto err_drm_fb_helper_fini;
69
70 return 0;
71
72 err_drm_fb_helper_fini:
73 drm_fb_helper_fini(fb_helper);
74 err_drm_err:
75 drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
76 return ret;
77 }
78
drm_fbdev_client_suspend(struct drm_client_dev * client)79 static int drm_fbdev_client_suspend(struct drm_client_dev *client)
80 {
81 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
82
83 drm_fb_helper_set_suspend_unlocked(fb_helper, true);
84
85 return 0;
86 }
87
drm_fbdev_client_resume(struct drm_client_dev * client)88 static int drm_fbdev_client_resume(struct drm_client_dev *client)
89 {
90 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
91
92 drm_fb_helper_set_suspend_unlocked(fb_helper, false);
93
94 return 0;
95 }
96
97 static const struct drm_client_funcs drm_fbdev_client_funcs = {
98 .owner = THIS_MODULE,
99 .free = drm_fbdev_client_free,
100 .unregister = drm_fbdev_client_unregister,
101 .restore = drm_fbdev_client_restore,
102 .hotplug = drm_fbdev_client_hotplug,
103 .suspend = drm_fbdev_client_suspend,
104 .resume = drm_fbdev_client_resume,
105 };
106
107 /**
108 * drm_fbdev_client_setup() - Setup fbdev emulation
109 * @dev: DRM device
110 * @format: Preferred color format for the device. DRM_FORMAT_XRGB8888
111 * is used if this is zero.
112 *
113 * This function sets up fbdev emulation. Restore, hotplug events and
114 * teardown are all taken care of. Drivers that do suspend/resume need
115 * to call drm_client_dev_suspend() and drm_client_dev_resume() by
116 * themselves. Simple drivers might use drm_mode_config_helper_suspend().
117 *
118 * This function is safe to call even when there are no connectors present.
119 * Setup will be retried on the next hotplug event.
120 *
121 * The fbdev client is destroyed by drm_dev_unregister().
122 *
123 * Returns:
124 * 0 on success, or a negative errno code otherwise.
125 */
drm_fbdev_client_setup(struct drm_device * dev,const struct drm_format_info * format)126 int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format)
127 {
128 struct drm_fb_helper *fb_helper;
129 unsigned int color_mode;
130 int ret;
131
132 /* TODO: Use format info throughout DRM */
133 if (format) {
134 unsigned int bpp = drm_format_info_bpp(format, 0);
135
136 switch (bpp) {
137 case 16:
138 color_mode = format->depth; // could also be 15
139 break;
140 default:
141 color_mode = bpp;
142 }
143 } else {
144 switch (dev->mode_config.preferred_depth) {
145 case 0:
146 case 24:
147 color_mode = 32;
148 break;
149 default:
150 color_mode = dev->mode_config.preferred_depth;
151 }
152 }
153
154 drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
155 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
156
157 fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
158 if (!fb_helper)
159 return -ENOMEM;
160 drm_fb_helper_prepare(dev, fb_helper, color_mode, NULL);
161
162 ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
163 if (ret) {
164 drm_err(dev, "Failed to register client: %d\n", ret);
165 goto err_drm_client_init;
166 }
167
168 drm_client_register(&fb_helper->client);
169
170 return 0;
171
172 err_drm_client_init:
173 drm_fb_helper_unprepare(fb_helper);
174 kfree(fb_helper);
175 return ret;
176 }
177