]> Git Repo - linux.git/blob - drivers/gpu/drm/drm_fbdev_shmem.c
Merge tag 'ti-k3-dt-for-v6.11-part2' into ti-k3-dts-next
[linux.git] / drivers / gpu / drm / drm_fbdev_shmem.c
1 // SPDX-License-Identifier: MIT
2
3 #include <linux/fb.h>
4
5 #include <drm/drm_crtc_helper.h>
6 #include <drm/drm_drv.h>
7 #include <drm/drm_fb_helper.h>
8 #include <drm/drm_framebuffer.h>
9 #include <drm/drm_gem_framebuffer_helper.h>
10 #include <drm/drm_gem_shmem_helper.h>
11
12 #include <drm/drm_fbdev_shmem.h>
13
14 /*
15  * struct fb_ops
16  */
17
18 static int drm_fbdev_shmem_fb_open(struct fb_info *info, int user)
19 {
20         struct drm_fb_helper *fb_helper = info->par;
21
22         /* No need to take a ref for fbcon because it unbinds on unregister */
23         if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
24                 return -ENODEV;
25
26         return 0;
27 }
28
29 static int drm_fbdev_shmem_fb_release(struct fb_info *info, int user)
30 {
31         struct drm_fb_helper *fb_helper = info->par;
32
33         if (user)
34                 module_put(fb_helper->dev->driver->fops->owner);
35
36         return 0;
37 }
38
39 FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_shmem,
40                                    drm_fb_helper_damage_range,
41                                    drm_fb_helper_damage_area);
42
43 static int drm_fbdev_shmem_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
44 {
45         struct drm_fb_helper *fb_helper = info->par;
46         struct drm_framebuffer *fb = fb_helper->fb;
47         struct drm_gem_object *obj = drm_gem_fb_get_obj(fb, 0);
48         struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
49
50         if (shmem->map_wc)
51                 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
52
53         return fb_deferred_io_mmap(info, vma);
54 }
55
56 static void drm_fbdev_shmem_fb_destroy(struct fb_info *info)
57 {
58         struct drm_fb_helper *fb_helper = info->par;
59
60         if (!fb_helper->dev)
61                 return;
62
63         fb_deferred_io_cleanup(info);
64         drm_fb_helper_fini(fb_helper);
65
66         drm_client_buffer_vunmap(fb_helper->buffer);
67         drm_client_framebuffer_delete(fb_helper->buffer);
68         drm_client_release(&fb_helper->client);
69         drm_fb_helper_unprepare(fb_helper);
70         kfree(fb_helper);
71 }
72
73 static const struct fb_ops drm_fbdev_shmem_fb_ops = {
74         .owner = THIS_MODULE,
75         .fb_open = drm_fbdev_shmem_fb_open,
76         .fb_release = drm_fbdev_shmem_fb_release,
77         __FB_DEFAULT_DEFERRED_OPS_RDWR(drm_fbdev_shmem),
78         DRM_FB_HELPER_DEFAULT_OPS,
79         __FB_DEFAULT_DEFERRED_OPS_DRAW(drm_fbdev_shmem),
80         .fb_mmap = drm_fbdev_shmem_fb_mmap,
81         .fb_destroy = drm_fbdev_shmem_fb_destroy,
82 };
83
84 static struct page *drm_fbdev_shmem_get_page(struct fb_info *info, unsigned long offset)
85 {
86         struct drm_fb_helper *fb_helper = info->par;
87         struct drm_framebuffer *fb = fb_helper->fb;
88         struct drm_gem_object *obj = drm_gem_fb_get_obj(fb, 0);
89         struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
90         unsigned int i = offset >> PAGE_SHIFT;
91         struct page *page;
92
93         if (fb_WARN_ON_ONCE(info, offset > obj->size))
94                 return NULL;
95
96         page = shmem->pages[i]; // protected by active vmap
97         if (page)
98                 get_page(page);
99         fb_WARN_ON_ONCE(info, !page);
100
101         return page;
102 }
103
104 /*
105  * struct drm_fb_helper
106  */
107
108 static int drm_fbdev_shmem_helper_fb_probe(struct drm_fb_helper *fb_helper,
109                                            struct drm_fb_helper_surface_size *sizes)
110 {
111         struct drm_client_dev *client = &fb_helper->client;
112         struct drm_device *dev = fb_helper->dev;
113         struct drm_client_buffer *buffer;
114         struct drm_gem_shmem_object *shmem;
115         struct drm_framebuffer *fb;
116         struct fb_info *info;
117         u32 format;
118         struct iosys_map map;
119         int ret;
120
121         drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
122                     sizes->surface_width, sizes->surface_height,
123                     sizes->surface_bpp);
124
125         format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp, sizes->surface_depth);
126         buffer = drm_client_framebuffer_create(client, sizes->surface_width,
127                                                sizes->surface_height, format);
128         if (IS_ERR(buffer))
129                 return PTR_ERR(buffer);
130         shmem = to_drm_gem_shmem_obj(buffer->gem);
131
132         fb = buffer->fb;
133
134         ret = drm_client_buffer_vmap(buffer, &map);
135         if (ret) {
136                 goto err_drm_client_buffer_delete;
137         } else if (drm_WARN_ON(dev, map.is_iomem)) {
138                 ret = -ENODEV; /* I/O memory not supported; use generic emulation */
139                 goto err_drm_client_buffer_delete;
140         }
141
142         fb_helper->buffer = buffer;
143         fb_helper->fb = fb;
144
145         info = drm_fb_helper_alloc_info(fb_helper);
146         if (IS_ERR(info)) {
147                 ret = PTR_ERR(info);
148                 goto err_drm_client_buffer_vunmap;
149         }
150
151         drm_fb_helper_fill_info(info, fb_helper, sizes);
152
153         info->fbops = &drm_fbdev_shmem_fb_ops;
154
155         /* screen */
156         info->flags |= FBINFO_VIRTFB; /* system memory */
157         if (!shmem->map_wc)
158                 info->flags |= FBINFO_READS_FAST; /* signal caching */
159         info->screen_size = sizes->surface_height * fb->pitches[0];
160         info->screen_buffer = map.vaddr;
161         info->fix.smem_len = info->screen_size;
162
163         /* deferred I/O */
164         fb_helper->fbdefio.delay = HZ / 20;
165         fb_helper->fbdefio.get_page = drm_fbdev_shmem_get_page;
166         fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
167
168         info->fbdefio = &fb_helper->fbdefio;
169         ret = fb_deferred_io_init(info);
170         if (ret)
171                 goto err_drm_fb_helper_release_info;
172
173         return 0;
174
175 err_drm_fb_helper_release_info:
176         drm_fb_helper_release_info(fb_helper);
177 err_drm_client_buffer_vunmap:
178         fb_helper->fb = NULL;
179         fb_helper->buffer = NULL;
180         drm_client_buffer_vunmap(buffer);
181 err_drm_client_buffer_delete:
182         drm_client_framebuffer_delete(buffer);
183         return ret;
184 }
185
186 static int drm_fbdev_shmem_helper_fb_dirty(struct drm_fb_helper *helper,
187                                            struct drm_clip_rect *clip)
188 {
189         struct drm_device *dev = helper->dev;
190         int ret;
191
192         /* Call damage handlers only if necessary */
193         if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))
194                 return 0;
195
196         if (helper->fb->funcs->dirty) {
197                 ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
198                 if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
199                         return ret;
200         }
201
202         return 0;
203 }
204
205 static const struct drm_fb_helper_funcs drm_fbdev_shmem_helper_funcs = {
206         .fb_probe = drm_fbdev_shmem_helper_fb_probe,
207         .fb_dirty = drm_fbdev_shmem_helper_fb_dirty,
208 };
209
210 /*
211  * struct drm_client_funcs
212  */
213
214 static void drm_fbdev_shmem_client_unregister(struct drm_client_dev *client)
215 {
216         struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
217
218         if (fb_helper->info) {
219                 drm_fb_helper_unregister_info(fb_helper);
220         } else {
221                 drm_client_release(&fb_helper->client);
222                 drm_fb_helper_unprepare(fb_helper);
223                 kfree(fb_helper);
224         }
225 }
226
227 static int drm_fbdev_shmem_client_restore(struct drm_client_dev *client)
228 {
229         drm_fb_helper_lastclose(client->dev);
230
231         return 0;
232 }
233
234 static int drm_fbdev_shmem_client_hotplug(struct drm_client_dev *client)
235 {
236         struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
237         struct drm_device *dev = client->dev;
238         int ret;
239
240         if (dev->fb_helper)
241                 return drm_fb_helper_hotplug_event(dev->fb_helper);
242
243         ret = drm_fb_helper_init(dev, fb_helper);
244         if (ret)
245                 goto err_drm_err;
246
247         if (!drm_drv_uses_atomic_modeset(dev))
248                 drm_helper_disable_unused_functions(dev);
249
250         ret = drm_fb_helper_initial_config(fb_helper);
251         if (ret)
252                 goto err_drm_fb_helper_fini;
253
254         return 0;
255
256 err_drm_fb_helper_fini:
257         drm_fb_helper_fini(fb_helper);
258 err_drm_err:
259         drm_err(dev, "fbdev-shmem: Failed to setup emulation (ret=%d)\n", ret);
260         return ret;
261 }
262
263 static const struct drm_client_funcs drm_fbdev_shmem_client_funcs = {
264         .owner          = THIS_MODULE,
265         .unregister     = drm_fbdev_shmem_client_unregister,
266         .restore        = drm_fbdev_shmem_client_restore,
267         .hotplug        = drm_fbdev_shmem_client_hotplug,
268 };
269
270 /**
271  * drm_fbdev_shmem_setup() - Setup fbdev emulation for GEM SHMEM helpers
272  * @dev: DRM device
273  * @preferred_bpp: Preferred bits per pixel for the device.
274  *                 32 is used if this is zero.
275  *
276  * This function sets up fbdev emulation for GEM DMA drivers that support
277  * dumb buffers with a virtual address and that can be mmap'ed.
278  * drm_fbdev_shmem_setup() shall be called after the DRM driver registered
279  * the new DRM device with drm_dev_register().
280  *
281  * Restore, hotplug events and teardown are all taken care of. Drivers that do
282  * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
283  * Simple drivers might use drm_mode_config_helper_suspend().
284  *
285  * This function is safe to call even when there are no connectors present.
286  * Setup will be retried on the next hotplug event.
287  *
288  * The fbdev is destroyed by drm_dev_unregister().
289  */
290 void drm_fbdev_shmem_setup(struct drm_device *dev, unsigned int preferred_bpp)
291 {
292         struct drm_fb_helper *fb_helper;
293         int ret;
294
295         drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
296         drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
297
298         fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
299         if (!fb_helper)
300                 return;
301         drm_fb_helper_prepare(dev, fb_helper, preferred_bpp, &drm_fbdev_shmem_helper_funcs);
302
303         ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_shmem_client_funcs);
304         if (ret) {
305                 drm_err(dev, "Failed to register client: %d\n", ret);
306                 goto err_drm_client_init;
307         }
308
309         drm_client_register(&fb_helper->client);
310
311         return;
312
313 err_drm_client_init:
314         drm_fb_helper_unprepare(fb_helper);
315         kfree(fb_helper);
316 }
317 EXPORT_SYMBOL(drm_fbdev_shmem_setup);
This page took 0.209662 seconds and 4 git commands to generate.