1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <drm/drmP.h>
18#include <drm/drm_crtc_helper.h>
19#include <drm/drm_fb_helper.h>
20#include <drm/drm_gem_cma_helper.h>
21
22#include "xylon_crtc.h"
23#include "xylon_drv.h"
24#include "xylon_fb.h"
25#include "xylon_fbdev.h"
26
27struct xylon_drm_fb_device {
28 struct drm_fb_helper fb_helper;
29};
30
31static struct fb_ops xylon_drm_fbdev_ops = {
32 .owner = THIS_MODULE,
33 .fb_fillrect = sys_fillrect,
34 .fb_copyarea = sys_copyarea,
35 .fb_imageblit = sys_imageblit,
36 .fb_check_var = drm_fb_helper_check_var,
37 .fb_set_par = drm_fb_helper_set_par,
38 .fb_blank = drm_fb_helper_blank,
39 .fb_pan_display = drm_fb_helper_pan_display,
40 .fb_setcmap = drm_fb_helper_setcmap,
41};
42
43static int xylon_drm_fbdev_create(struct drm_fb_helper *helper,
44 struct drm_fb_helper_surface_size *sizes)
45{
46 struct drm_device *dev = helper->dev;
47 struct drm_framebuffer *fb;
48 struct drm_gem_cma_object *obj;
49 struct drm_mode_fb_cmd2 mode_cmd;
50 struct fb_info *fbi;
51 struct xylon_drm_device *xdev = dev->dev_private;
52 unsigned long offset;
53 unsigned int bytes_per_pixel;
54 unsigned int buff_width;
55 size_t size;
56 int ret;
57
58 ret = xylon_drm_crtc_get_param(xdev->crtc, &buff_width,
59 XYLON_DRM_CRTC_BUFF_WIDTH);
60 if (ret)
61 return ret;
62
63 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
64
65 memset(&mode_cmd, 0, sizeof(mode_cmd));
66
67 mode_cmd.width = sizes->surface_width;
68 mode_cmd.height = sizes->surface_height;
69 mode_cmd.pitches[0] = buff_width * bytes_per_pixel;
70 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
71 sizes->surface_depth);
72
73 size = mode_cmd.pitches[0] * mode_cmd.height;
74
75 obj = drm_gem_cma_create(dev, size);
76 if (IS_ERR(obj))
77 return -ENOMEM;
78
79 fb = xylon_drm_fb_init(dev, &mode_cmd, &obj->base);
80 if (IS_ERR(fb)) {
81 DRM_ERROR("failed initialize fb\n");
82 goto err_fb_init;
83 }
84
85 fbi = framebuffer_alloc(0, dev->dev);
86 if (!fbi) {
87 DRM_ERROR("failed allocate framebuffer info\n");
88 ret = -ENOMEM;
89 goto err_fb_alloc;
90 }
91
92 helper->fb = fb;
93 helper->fbdev = fbi;
94
95 fbi->par = helper;
96 fbi->flags = FBINFO_FLAG_DEFAULT;
97 fbi->fbops = &xylon_drm_fbdev_ops;
98
99 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
100 if (ret) {
101 DRM_ERROR("failed allocate color map\n");
102 goto err_fb_alloc_cmap;
103 }
104
105 drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
106 drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
107
108 offset = fbi->var.xoffset * bytes_per_pixel;
109 offset += fbi->var.yoffset * fb->pitches[0];
110
111 dev->mode_config.fb_base = (resource_size_t)obj->paddr;
112 fbi->screen_base = (char __iomem *)(obj->vaddr + offset);
113 fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
114 fbi->screen_size = size;
115 fbi->fix.smem_len = size;
116
117 return 0;
118
119err_fb_alloc_cmap:
120 drm_framebuffer_unregister_private(fb);
121 drm_framebuffer_remove(fb);
122err_fb_init:
123 framebuffer_release(fbi);
124err_fb_alloc:
125 drm_gem_cma_free_object(&obj->base);
126
127 return ret;
128}
129
130static const struct drm_fb_helper_funcs xylon_drm_fbdev_helper_funcs = {
131 .fb_probe = xylon_drm_fbdev_create,
132};
133
134struct xylon_drm_fb_device *
135xylon_drm_fbdev_init(struct drm_device *dev,
136 unsigned int preferred_bpp, unsigned int num_crtc,
137 unsigned int max_conn_count)
138{
139 struct drm_fb_helper *helper;
140 struct xylon_drm_fb_device *fbdev;
141 int ret;
142
143 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
144 if (!fbdev) {
145 DRM_ERROR("failed allocate fbdev\n");
146 return ERR_PTR(-ENOMEM);
147 }
148
149 helper = &fbdev->fb_helper;
150
151 drm_fb_helper_prepare(dev, helper, &xylon_drm_fbdev_helper_funcs);
152
153 ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
154 if (ret < 0) {
155 DRM_ERROR("failed fb init\n");
156 goto err_fb_helper_init;
157 }
158
159 ret = drm_fb_helper_single_add_all_connectors(helper);
160 if (ret < 0) {
161 DRM_ERROR("failed add connectors\n");
162 goto err_fb_helper_single_add;
163 }
164
165 drm_helper_disable_unused_functions(dev);
166
167 if (drm_fb_helper_initial_config(helper, preferred_bpp)) {
168 DRM_ERROR("failed fb initial config\n");
169 ret = -EINVAL;
170 goto err_fb_helper_single_add;
171 }
172
173 return fbdev;
174
175err_fb_helper_single_add:
176 drm_fb_helper_fini(helper);
177err_fb_helper_init:
178 kfree(fbdev);
179
180 return ERR_PTR(ret);
181}
182
183void xylon_drm_fbdev_fini(struct xylon_drm_fb_device *fbdev)
184{
185 struct fb_info *info;
186 int ret;
187
188 if (fbdev->fb_helper.fbdev) {
189 info = fbdev->fb_helper.fbdev;
190
191 ret = unregister_framebuffer(info);
192 if (ret < 0)
193 DRM_INFO("failed unregister fb\n");
194
195 if (info->cmap.len)
196 fb_dealloc_cmap(&info->cmap);
197
198 framebuffer_release(info);
199 }
200
201 drm_framebuffer_unregister_private(fbdev->fb_helper.fb);
202 drm_framebuffer_remove(fbdev->fb_helper.fb);
203
204 drm_fb_helper_fini(&fbdev->fb_helper);
205
206 kfree(fbdev);
207}
208
209void xylon_drm_fbdev_restore_mode(struct xylon_drm_fb_device *fbdev)
210{
211 struct drm_device *dev;
212
213 if (fbdev) {
214 dev = fbdev->fb_helper.dev;
215
216 drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->fb_helper);
217 }
218}
219
220void xylon_drm_fbdev_hotplug_event(struct xylon_drm_fb_device *fbdev)
221{
222 if (fbdev)
223 drm_fb_helper_hotplug_event(&fbdev->fb_helper);
224}
225