1 // SPDX-License-Identifier: MIT
5 #include <drm/drm_drv.h>
6 #include <drm/drm_fbdev_shmem.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>
16 static int drm_fbdev_shmem_fb_open(struct fb_info
*info
, int user
)
18 struct drm_fb_helper
*fb_helper
= info
->par
;
20 /* No need to take a ref for fbcon because it unbinds on unregister */
21 if (user
&& !try_module_get(fb_helper
->dev
->driver
->fops
->owner
))
27 static int drm_fbdev_shmem_fb_release(struct fb_info
*info
, int user
)
29 struct drm_fb_helper
*fb_helper
= info
->par
;
32 module_put(fb_helper
->dev
->driver
->fops
->owner
);
37 FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_shmem
,
38 drm_fb_helper_damage_range
,
39 drm_fb_helper_damage_area
);
41 static int drm_fbdev_shmem_fb_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
)
43 struct drm_fb_helper
*fb_helper
= info
->par
;
44 struct drm_framebuffer
*fb
= fb_helper
->fb
;
45 struct drm_gem_object
*obj
= drm_gem_fb_get_obj(fb
, 0);
46 struct drm_gem_shmem_object
*shmem
= to_drm_gem_shmem_obj(obj
);
49 vma
->vm_page_prot
= pgprot_writecombine(vma
->vm_page_prot
);
51 return fb_deferred_io_mmap(info
, vma
);
54 static void drm_fbdev_shmem_fb_destroy(struct fb_info
*info
)
56 struct drm_fb_helper
*fb_helper
= info
->par
;
61 fb_deferred_io_cleanup(info
);
62 drm_fb_helper_fini(fb_helper
);
64 drm_client_buffer_vunmap(fb_helper
->buffer
);
65 drm_client_framebuffer_delete(fb_helper
->buffer
);
66 drm_client_release(&fb_helper
->client
);
67 drm_fb_helper_unprepare(fb_helper
);
71 static const struct fb_ops drm_fbdev_shmem_fb_ops
= {
73 .fb_open
= drm_fbdev_shmem_fb_open
,
74 .fb_release
= drm_fbdev_shmem_fb_release
,
75 __FB_DEFAULT_DEFERRED_OPS_RDWR(drm_fbdev_shmem
),
76 DRM_FB_HELPER_DEFAULT_OPS
,
77 __FB_DEFAULT_DEFERRED_OPS_DRAW(drm_fbdev_shmem
),
78 .fb_mmap
= drm_fbdev_shmem_fb_mmap
,
79 .fb_destroy
= drm_fbdev_shmem_fb_destroy
,
82 static struct page
*drm_fbdev_shmem_get_page(struct fb_info
*info
, unsigned long offset
)
84 struct drm_fb_helper
*fb_helper
= info
->par
;
85 struct drm_framebuffer
*fb
= fb_helper
->fb
;
86 struct drm_gem_object
*obj
= drm_gem_fb_get_obj(fb
, 0);
87 struct drm_gem_shmem_object
*shmem
= to_drm_gem_shmem_obj(obj
);
88 unsigned int i
= offset
>> PAGE_SHIFT
;
91 if (fb_WARN_ON_ONCE(info
, offset
> obj
->size
))
94 page
= shmem
->pages
[i
]; // protected by active vmap
97 fb_WARN_ON_ONCE(info
, !page
);
103 * struct drm_fb_helper
106 static int drm_fbdev_shmem_helper_fb_dirty(struct drm_fb_helper
*helper
,
107 struct drm_clip_rect
*clip
)
109 struct drm_device
*dev
= helper
->dev
;
112 /* Call damage handlers only if necessary */
113 if (!(clip
->x1
< clip
->x2
&& clip
->y1
< clip
->y2
))
116 if (helper
->fb
->funcs
->dirty
) {
117 ret
= helper
->fb
->funcs
->dirty(helper
->fb
, NULL
, 0, 0, clip
, 1);
118 if (drm_WARN_ONCE(dev
, ret
, "Dirty helper failed: ret=%d\n", ret
))
125 static const struct drm_fb_helper_funcs drm_fbdev_shmem_helper_funcs
= {
126 .fb_dirty
= drm_fbdev_shmem_helper_fb_dirty
,
133 int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper
*fb_helper
,
134 struct drm_fb_helper_surface_size
*sizes
)
136 struct drm_client_dev
*client
= &fb_helper
->client
;
137 struct drm_device
*dev
= fb_helper
->dev
;
138 struct drm_client_buffer
*buffer
;
139 struct drm_gem_shmem_object
*shmem
;
140 struct drm_framebuffer
*fb
;
141 struct fb_info
*info
;
143 struct iosys_map map
;
146 drm_dbg_kms(dev
, "surface width(%d), height(%d) and bpp(%d)\n",
147 sizes
->surface_width
, sizes
->surface_height
,
150 format
= drm_driver_legacy_fb_format(dev
, sizes
->surface_bpp
, sizes
->surface_depth
);
151 buffer
= drm_client_framebuffer_create(client
, sizes
->surface_width
,
152 sizes
->surface_height
, format
);
154 return PTR_ERR(buffer
);
155 shmem
= to_drm_gem_shmem_obj(buffer
->gem
);
159 ret
= drm_client_buffer_vmap(buffer
, &map
);
161 goto err_drm_client_buffer_delete
;
162 } else if (drm_WARN_ON(dev
, map
.is_iomem
)) {
163 ret
= -ENODEV
; /* I/O memory not supported; use generic emulation */
164 goto err_drm_client_buffer_delete
;
167 fb_helper
->funcs
= &drm_fbdev_shmem_helper_funcs
;
168 fb_helper
->buffer
= buffer
;
171 info
= drm_fb_helper_alloc_info(fb_helper
);
174 goto err_drm_client_buffer_vunmap
;
177 drm_fb_helper_fill_info(info
, fb_helper
, sizes
);
179 info
->fbops
= &drm_fbdev_shmem_fb_ops
;
182 info
->flags
|= FBINFO_VIRTFB
; /* system memory */
184 info
->flags
|= FBINFO_READS_FAST
; /* signal caching */
185 info
->screen_size
= sizes
->surface_height
* fb
->pitches
[0];
186 info
->screen_buffer
= map
.vaddr
;
187 info
->fix
.smem_len
= info
->screen_size
;
190 fb_helper
->fbdefio
.delay
= HZ
/ 20;
191 fb_helper
->fbdefio
.get_page
= drm_fbdev_shmem_get_page
;
192 fb_helper
->fbdefio
.deferred_io
= drm_fb_helper_deferred_io
;
194 info
->fbdefio
= &fb_helper
->fbdefio
;
195 ret
= fb_deferred_io_init(info
);
197 goto err_drm_fb_helper_release_info
;
201 err_drm_fb_helper_release_info
:
202 drm_fb_helper_release_info(fb_helper
);
203 err_drm_client_buffer_vunmap
:
204 fb_helper
->fb
= NULL
;
205 fb_helper
->buffer
= NULL
;
206 drm_client_buffer_vunmap(buffer
);
207 err_drm_client_buffer_delete
:
208 drm_client_framebuffer_delete(buffer
);
211 EXPORT_SYMBOL(drm_fbdev_shmem_driver_fbdev_probe
);