]> Git Repo - linux.git/blob - drivers/gpu/drm/omapdrm/omap_fbdev.c
Merge tag 'printk-for-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/printk...
[linux.git] / drivers / gpu / drm / omapdrm / omap_fbdev.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
4  * Author: Rob Clark <[email protected]>
5  */
6
7 #include <linux/fb.h>
8
9 #include <drm/drm_drv.h>
10 #include <drm/drm_crtc_helper.h>
11 #include <drm/drm_fb_helper.h>
12 #include <drm/drm_file.h>
13 #include <drm/drm_fourcc.h>
14 #include <drm/drm_framebuffer.h>
15 #include <drm/drm_gem_framebuffer_helper.h>
16 #include <drm/drm_util.h>
17
18 #include "omap_drv.h"
19 #include "omap_fbdev.h"
20
21 MODULE_PARM_DESC(ywrap, "Enable ywrap scrolling (omap44xx and later, default 'y')");
22 static bool ywrap_enabled = true;
23 module_param_named(ywrap, ywrap_enabled, bool, 0644);
24
25 /*
26  * fbdev funcs, to implement legacy fbdev interface on top of drm driver
27  */
28
29 #define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
30
31 struct omap_fbdev {
32         struct drm_fb_helper base;
33         bool ywrap_enabled;
34
35         /* for deferred dmm roll when getting called in atomic ctx */
36         struct work_struct work;
37 };
38
39 static struct drm_fb_helper *get_fb(struct fb_info *fbi);
40
41 static void pan_worker(struct work_struct *work)
42 {
43         struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
44         struct drm_fb_helper *helper = &fbdev->base;
45         struct fb_info *fbi = helper->info;
46         struct drm_gem_object *bo = drm_gem_fb_get_obj(helper->fb, 0);
47         int npages;
48
49         /* DMM roll shifts in 4K pages: */
50         npages = fbi->fix.line_length >> PAGE_SHIFT;
51         omap_gem_roll(bo, fbi->var.yoffset * npages);
52 }
53
54 static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
55                 struct fb_info *fbi)
56 {
57         struct drm_fb_helper *helper = get_fb(fbi);
58         struct omap_fbdev *fbdev = to_omap_fbdev(helper);
59
60         if (!helper)
61                 goto fallback;
62
63         if (!fbdev->ywrap_enabled)
64                 goto fallback;
65
66         if (drm_can_sleep()) {
67                 pan_worker(&fbdev->work);
68         } else {
69                 struct omap_drm_private *priv = helper->dev->dev_private;
70                 queue_work(priv->wq, &fbdev->work);
71         }
72
73         return 0;
74
75 fallback:
76         return drm_fb_helper_pan_display(var, fbi);
77 }
78
79 static int omap_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
80 {
81         struct drm_fb_helper *helper = info->par;
82         struct drm_framebuffer *fb = helper->fb;
83         struct drm_gem_object *bo = drm_gem_fb_get_obj(fb, 0);
84
85         return drm_gem_mmap_obj(bo, omap_gem_mmap_size(bo), vma);
86 }
87
88 static void omap_fbdev_fb_destroy(struct fb_info *info)
89 {
90         struct drm_fb_helper *helper = info->par;
91         struct drm_framebuffer *fb = helper->fb;
92         struct drm_gem_object *bo = drm_gem_fb_get_obj(fb, 0);
93         struct omap_fbdev *fbdev = to_omap_fbdev(helper);
94
95         DBG();
96
97         drm_fb_helper_fini(helper);
98
99         omap_gem_unpin(bo);
100         drm_framebuffer_remove(fb);
101
102         drm_client_release(&helper->client);
103         drm_fb_helper_unprepare(helper);
104         kfree(fbdev);
105 }
106
107 static const struct fb_ops omap_fb_ops = {
108         .owner = THIS_MODULE,
109         __FB_DEFAULT_DMAMEM_OPS_RDWR,
110         .fb_check_var   = drm_fb_helper_check_var,
111         .fb_set_par     = drm_fb_helper_set_par,
112         .fb_setcmap     = drm_fb_helper_setcmap,
113         .fb_blank       = drm_fb_helper_blank,
114         .fb_pan_display = omap_fbdev_pan_display,
115         __FB_DEFAULT_DMAMEM_OPS_DRAW,
116         .fb_ioctl       = drm_fb_helper_ioctl,
117         .fb_mmap        = omap_fbdev_fb_mmap,
118         .fb_destroy     = omap_fbdev_fb_destroy,
119 };
120
121 static int omap_fbdev_create(struct drm_fb_helper *helper,
122                 struct drm_fb_helper_surface_size *sizes)
123 {
124         struct omap_fbdev *fbdev = to_omap_fbdev(helper);
125         struct drm_device *dev = helper->dev;
126         struct omap_drm_private *priv = dev->dev_private;
127         struct drm_framebuffer *fb = NULL;
128         union omap_gem_size gsize;
129         struct fb_info *fbi = NULL;
130         struct drm_mode_fb_cmd2 mode_cmd = {0};
131         struct drm_gem_object *bo;
132         dma_addr_t dma_addr;
133         int ret;
134
135         sizes->surface_bpp = 32;
136         sizes->surface_depth = 24;
137
138         DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
139                         sizes->surface_height, sizes->surface_bpp,
140                         sizes->fb_width, sizes->fb_height);
141
142         mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
143                         sizes->surface_depth);
144
145         mode_cmd.width = sizes->surface_width;
146         mode_cmd.height = sizes->surface_height;
147
148         mode_cmd.pitches[0] =
149                         DIV_ROUND_UP(mode_cmd.width * sizes->surface_bpp, 8);
150
151         fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled;
152         if (fbdev->ywrap_enabled) {
153                 /* need to align pitch to page size if using DMM scrolling */
154                 mode_cmd.pitches[0] = PAGE_ALIGN(mode_cmd.pitches[0]);
155         }
156
157         /* allocate backing bo */
158         gsize = (union omap_gem_size){
159                 .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height),
160         };
161         DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index);
162         bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC);
163         if (!bo) {
164                 dev_err(dev->dev, "failed to allocate buffer object\n");
165                 ret = -ENOMEM;
166                 goto fail;
167         }
168
169         fb = omap_framebuffer_init(dev, &mode_cmd, &bo);
170         if (IS_ERR(fb)) {
171                 dev_err(dev->dev, "failed to allocate fb\n");
172                 /* note: if fb creation failed, we can't rely on fb destroy
173                  * to unref the bo:
174                  */
175                 drm_gem_object_put(bo);
176                 ret = PTR_ERR(fb);
177                 goto fail;
178         }
179
180         /* note: this keeps the bo pinned.. which is perhaps not ideal,
181          * but is needed as long as we use fb_mmap() to mmap to userspace
182          * (since this happens using fix.smem_start).  Possibly we could
183          * implement our own mmap using GEM mmap support to avoid this
184          * (non-tiled buffer doesn't need to be pinned for fbcon to write
185          * to it).  Then we just need to be sure that we are able to re-
186          * pin it in case of an opps.
187          */
188         ret = omap_gem_pin(bo, &dma_addr);
189         if (ret) {
190                 dev_err(dev->dev, "could not pin framebuffer\n");
191                 ret = -ENOMEM;
192                 goto fail;
193         }
194
195         fbi = drm_fb_helper_alloc_info(helper);
196         if (IS_ERR(fbi)) {
197                 dev_err(dev->dev, "failed to allocate fb info\n");
198                 ret = PTR_ERR(fbi);
199                 goto fail;
200         }
201
202         DBG("fbi=%p, dev=%p", fbi, dev);
203
204         helper->fb = fb;
205
206         fbi->fbops = &omap_fb_ops;
207
208         drm_fb_helper_fill_info(fbi, helper, sizes);
209
210         fbi->flags |= FBINFO_VIRTFB;
211         fbi->screen_buffer = omap_gem_vaddr(bo);
212         fbi->screen_size = bo->size;
213         fbi->fix.smem_start = dma_addr;
214         fbi->fix.smem_len = bo->size;
215
216         /* if we have DMM, then we can use it for scrolling by just
217          * shuffling pages around in DMM rather than doing sw blit.
218          */
219         if (fbdev->ywrap_enabled) {
220                 DRM_INFO("Enabling DMM ywrap scrolling\n");
221                 fbi->flags |= FBINFO_HWACCEL_YWRAP | FBINFO_READS_FAST;
222                 fbi->fix.ywrapstep = 1;
223         }
224
225
226         DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
227         DBG("allocated %dx%d fb", fb->width, fb->height);
228
229         return 0;
230
231 fail:
232
233         if (ret) {
234                 if (fb)
235                         drm_framebuffer_remove(fb);
236         }
237
238         return ret;
239 }
240
241 static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
242         .fb_probe = omap_fbdev_create,
243 };
244
245 static struct drm_fb_helper *get_fb(struct fb_info *fbi)
246 {
247         if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
248                 /* these are not the fb's you're looking for */
249                 return NULL;
250         }
251         return fbi->par;
252 }
253
254 /*
255  * struct drm_client
256  */
257
258 static void omap_fbdev_client_unregister(struct drm_client_dev *client)
259 {
260         struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
261
262         if (fb_helper->info) {
263                 drm_fb_helper_unregister_info(fb_helper);
264         } else {
265                 drm_client_release(&fb_helper->client);
266                 drm_fb_helper_unprepare(fb_helper);
267                 kfree(fb_helper);
268         }
269 }
270
271 static int omap_fbdev_client_restore(struct drm_client_dev *client)
272 {
273         drm_fb_helper_lastclose(client->dev);
274
275         return 0;
276 }
277
278 static int omap_fbdev_client_hotplug(struct drm_client_dev *client)
279 {
280         struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
281         struct drm_device *dev = client->dev;
282         int ret;
283
284         if (dev->fb_helper)
285                 return drm_fb_helper_hotplug_event(dev->fb_helper);
286
287         ret = drm_fb_helper_init(dev, fb_helper);
288         if (ret)
289                 goto err_drm_err;
290
291         ret = drm_fb_helper_initial_config(fb_helper);
292         if (ret)
293                 goto err_drm_fb_helper_fini;
294
295         return 0;
296
297 err_drm_fb_helper_fini:
298         drm_fb_helper_fini(fb_helper);
299 err_drm_err:
300         drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret);
301         return ret;
302 }
303
304 static const struct drm_client_funcs omap_fbdev_client_funcs = {
305         .owner          = THIS_MODULE,
306         .unregister     = omap_fbdev_client_unregister,
307         .restore        = omap_fbdev_client_restore,
308         .hotplug        = omap_fbdev_client_hotplug,
309 };
310
311 void omap_fbdev_setup(struct drm_device *dev)
312 {
313         struct omap_fbdev *fbdev;
314         struct drm_fb_helper *helper;
315         int ret;
316
317         drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
318         drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
319
320         fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
321         if (!fbdev)
322                 return;
323         helper = &fbdev->base;
324
325         drm_fb_helper_prepare(dev, helper, 32, &omap_fb_helper_funcs);
326
327         ret = drm_client_init(dev, &helper->client, "fbdev", &omap_fbdev_client_funcs);
328         if (ret)
329                 goto err_drm_client_init;
330
331         INIT_WORK(&fbdev->work, pan_worker);
332
333         drm_client_register(&helper->client);
334
335         return;
336
337 err_drm_client_init:
338         drm_fb_helper_unprepare(helper);
339         kfree(fbdev);
340 }
This page took 0.053628 seconds and 4 git commands to generate.