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_fbdev_client.h>
7 #include <drm/drm_fb_helper.h>
8 #include <drm/drm_fourcc.h>
9 #include <drm/drm_print.h>
10
11 /*
12 * struct drm_client_funcs
13 */
14
drm_fbdev_client_unregister(struct drm_client_dev * client)15 static void drm_fbdev_client_unregister(struct drm_client_dev *client)
16 {
17 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
18
19 if (fb_helper->info) {
20 drm_fb_helper_unregister_info(fb_helper);
21 } else {
22 drm_client_release(&fb_helper->client);
23 drm_fb_helper_unprepare(fb_helper);
24 kfree(fb_helper);
25 }
26 }
27
drm_fbdev_client_restore(struct drm_client_dev * client)28 static int drm_fbdev_client_restore(struct drm_client_dev *client)
29 {
30 drm_fb_helper_lastclose(client->dev);
31
32 return 0;
33 }
34
drm_fbdev_client_hotplug(struct drm_client_dev * client)35 static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
36 {
37 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
38 struct drm_device *dev = client->dev;
39 int ret;
40
41 if (dev->fb_helper)
42 return drm_fb_helper_hotplug_event(dev->fb_helper);
43
44 ret = drm_fb_helper_init(dev, fb_helper);
45 if (ret)
46 goto err_drm_err;
47
48 if (!drm_drv_uses_atomic_modeset(dev))
49 drm_helper_disable_unused_functions(dev);
50
51 ret = drm_fb_helper_initial_config(fb_helper);
52 if (ret)
53 goto err_drm_fb_helper_fini;
54
55 return 0;
56
57 err_drm_fb_helper_fini:
58 drm_fb_helper_fini(fb_helper);
59 err_drm_err:
60 drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
61 return ret;
62 }
63
64 static const struct drm_client_funcs drm_fbdev_client_funcs = {
65 .owner = THIS_MODULE,
66 .unregister = drm_fbdev_client_unregister,
67 .restore = drm_fbdev_client_restore,
68 .hotplug = drm_fbdev_client_hotplug,
69 };
70
71 /**
72 * drm_fbdev_client_setup() - Setup fbdev emulation
73 * @dev: DRM device
74 * @format: Preferred color format for the device. DRM_FORMAT_XRGB8888
75 * is used if this is zero.
76 *
77 * This function sets up fbdev emulation. Restore, hotplug events and
78 * teardown are all taken care of. Drivers that do suspend/resume need
79 * to call drm_fb_helper_set_suspend_unlocked() themselves. Simple
80 * drivers might use drm_mode_config_helper_suspend().
81 *
82 * This function is safe to call even when there are no connectors present.
83 * Setup will be retried on the next hotplug event.
84 *
85 * The fbdev client is destroyed by drm_dev_unregister().
86 *
87 * Returns:
88 * 0 on success, or a negative errno code otherwise.
89 */
drm_fbdev_client_setup(struct drm_device * dev,const struct drm_format_info * format)90 int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format)
91 {
92 struct drm_fb_helper *fb_helper;
93 unsigned int color_mode;
94 int ret;
95
96 /* TODO: Use format info throughout DRM */
97 if (format) {
98 unsigned int bpp = drm_format_info_bpp(format, 0);
99
100 switch (bpp) {
101 case 16:
102 color_mode = format->depth; // could also be 15
103 break;
104 default:
105 color_mode = bpp;
106 }
107 } else {
108 switch (dev->mode_config.preferred_depth) {
109 case 0:
110 case 24:
111 color_mode = 32;
112 break;
113 default:
114 color_mode = dev->mode_config.preferred_depth;
115 }
116 }
117
118 drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
119 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
120
121 fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
122 if (!fb_helper)
123 return -ENOMEM;
124 drm_fb_helper_prepare(dev, fb_helper, color_mode, NULL);
125
126 ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
127 if (ret) {
128 drm_err(dev, "Failed to register client: %d\n", ret);
129 goto err_drm_client_init;
130 }
131
132 drm_client_register(&fb_helper->client);
133
134 return 0;
135
136 err_drm_client_init:
137 drm_fb_helper_unprepare(fb_helper);
138 kfree(fb_helper);
139 return ret;
140 }
141 EXPORT_SYMBOL(drm_fbdev_client_setup);
142