]> Git Repo - linux.git/blob - drivers/gpu/drm/drm_fbdev_client.c
Merge tag 'nfs-for-6.13-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[linux.git] / drivers / gpu / drm / drm_fbdev_client.c
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
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
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
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 int drm_fbdev_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
65 {
66         struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
67
68         if (holds_console_lock)
69                 drm_fb_helper_set_suspend(fb_helper, true);
70         else
71                 drm_fb_helper_set_suspend_unlocked(fb_helper, true);
72
73         return 0;
74 }
75
76 static int drm_fbdev_client_resume(struct drm_client_dev *client, bool holds_console_lock)
77 {
78         struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
79
80         if (holds_console_lock)
81                 drm_fb_helper_set_suspend(fb_helper, false);
82         else
83                 drm_fb_helper_set_suspend_unlocked(fb_helper, false);
84
85         return 0;
86 }
87
88 static const struct drm_client_funcs drm_fbdev_client_funcs = {
89         .owner          = THIS_MODULE,
90         .unregister     = drm_fbdev_client_unregister,
91         .restore        = drm_fbdev_client_restore,
92         .hotplug        = drm_fbdev_client_hotplug,
93         .suspend        = drm_fbdev_client_suspend,
94         .resume         = drm_fbdev_client_resume,
95 };
96
97 /**
98  * drm_fbdev_client_setup() - Setup fbdev emulation
99  * @dev: DRM device
100  * @format: Preferred color format for the device. DRM_FORMAT_XRGB8888
101  *          is used if this is zero.
102  *
103  * This function sets up fbdev emulation. Restore, hotplug events and
104  * teardown are all taken care of. Drivers that do suspend/resume need
105  * to call drm_client_dev_suspend() and drm_client_dev_resume() by
106  * themselves. Simple drivers might use drm_mode_config_helper_suspend().
107  *
108  * This function is safe to call even when there are no connectors present.
109  * Setup will be retried on the next hotplug event.
110  *
111  * The fbdev client is destroyed by drm_dev_unregister().
112  *
113  * Returns:
114  * 0 on success, or a negative errno code otherwise.
115  */
116 int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format)
117 {
118         struct drm_fb_helper *fb_helper;
119         unsigned int color_mode;
120         int ret;
121
122         /* TODO: Use format info throughout DRM */
123         if (format) {
124                 unsigned int bpp = drm_format_info_bpp(format, 0);
125
126                 switch (bpp) {
127                 case 16:
128                         color_mode = format->depth; // could also be 15
129                         break;
130                 default:
131                         color_mode = bpp;
132                 }
133         } else {
134                 switch (dev->mode_config.preferred_depth) {
135                 case 0:
136                 case 24:
137                         color_mode = 32;
138                         break;
139                 default:
140                         color_mode = dev->mode_config.preferred_depth;
141                 }
142         }
143
144         drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
145         drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
146
147         fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
148         if (!fb_helper)
149                 return -ENOMEM;
150         drm_fb_helper_prepare(dev, fb_helper, color_mode, NULL);
151
152         ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
153         if (ret) {
154                 drm_err(dev, "Failed to register client: %d\n", ret);
155                 goto err_drm_client_init;
156         }
157
158         drm_client_register(&fb_helper->client);
159
160         return 0;
161
162 err_drm_client_init:
163         drm_fb_helper_unprepare(fb_helper);
164         kfree(fb_helper);
165         return ret;
166 }
167 EXPORT_SYMBOL(drm_fbdev_client_setup);
This page took 0.042551 seconds and 4 git commands to generate.