Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
16static 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
24static 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
41static 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
50static 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
72err_drm_fb_helper_fini:
73 drm_fb_helper_fini(fb_helper);
74err_drm_err:
75 drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
76 return ret;
77}
78
79static 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
88static 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
97static 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 */
126int 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
172err_drm_client_init:
173 drm_fb_helper_unprepare(fb_helper);
174 kfree(fb_helper);
175 return ret;
176}