aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/cirrus/cirrus_fbdev.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2012-04-17 09:12:29 -0400
committerDave Airlie <airlied@redhat.com>2012-05-17 06:02:24 -0400
commitf9aa76a85248565ff13dc0e10633c4343a059f71 (patch)
tree23cb9a78412f33af72bea500c2027bb3d6706f1f /drivers/gpu/drm/cirrus/cirrus_fbdev.c
parent414c453106255b11df77ed6b08eedb6d2369c338 (diff)
drm/kms: driver for virtual cirrus under qemu
This is the initial driver for emulated cirrus GPU found in qemu. This driver only supports the emulated GPU and doesn't attempt to bind to any real cirrus GPUs. This driver is intended to be used with xf86-video-modesetting in userspace. It requires at least version 0.3.0 This follow the same design as ast and mgag200, and is based on work done by Matthew Garrett previously. This GPU has no hw cursor, and it can't scanout 32-bpp, only packed 24-bpp. i.e. it sucks. Reviewed-by: Adam Jackson <ajax@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/cirrus/cirrus_fbdev.c')
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_fbdev.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
new file mode 100644
index 000000000000..9a276a536992
--- /dev/null
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -0,0 +1,307 @@
1/*
2 * Copyright 2012 Red Hat
3 *
4 * This file is subject to the terms and conditions of the GNU General
5 * Public License version 2. See the file COPYING in the main
6 * directory of this archive for more details.
7 *
8 * Authors: Matthew Garrett
9 * Dave Airlie
10 */
11#include <linux/module.h>
12#include "drmP.h"
13#include "drm.h"
14#include "drm_fb_helper.h"
15
16#include <linux/fb.h>
17
18#include "cirrus_drv.h"
19
20static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
21 int x, int y, int width, int height)
22{
23 int i;
24 struct drm_gem_object *obj;
25 struct cirrus_bo *bo;
26 int src_offset, dst_offset;
27 int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8;
28 int ret;
29 bool unmap = false;
30
31 obj = afbdev->gfb.obj;
32 bo = gem_to_cirrus_bo(obj);
33
34 ret = cirrus_bo_reserve(bo, true);
35 if (ret) {
36 DRM_ERROR("failed to reserve fb bo\n");
37 return;
38 }
39
40 if (!bo->kmap.virtual) {
41 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
42 if (ret) {
43 DRM_ERROR("failed to kmap fb updates\n");
44 cirrus_bo_unreserve(bo);
45 return;
46 }
47 unmap = true;
48 }
49 for (i = y; i < y + height; i++) {
50 /* assume equal stride for now */
51 src_offset = dst_offset = i * afbdev->gfb.base.pitches[0] + (x * bpp);
52 memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
53
54 }
55 if (unmap)
56 ttm_bo_kunmap(&bo->kmap);
57
58 cirrus_bo_unreserve(bo);
59}
60
61static void cirrus_fillrect(struct fb_info *info,
62 const struct fb_fillrect *rect)
63{
64 struct cirrus_fbdev *afbdev = info->par;
65 sys_fillrect(info, rect);
66 cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width,
67 rect->height);
68}
69
70static void cirrus_copyarea(struct fb_info *info,
71 const struct fb_copyarea *area)
72{
73 struct cirrus_fbdev *afbdev = info->par;
74 sys_copyarea(info, area);
75 cirrus_dirty_update(afbdev, area->dx, area->dy, area->width,
76 area->height);
77}
78
79static void cirrus_imageblit(struct fb_info *info,
80 const struct fb_image *image)
81{
82 struct cirrus_fbdev *afbdev = info->par;
83 sys_imageblit(info, image);
84 cirrus_dirty_update(afbdev, image->dx, image->dy, image->width,
85 image->height);
86}
87
88
89static struct fb_ops cirrusfb_ops = {
90 .owner = THIS_MODULE,
91 .fb_check_var = drm_fb_helper_check_var,
92 .fb_set_par = drm_fb_helper_set_par,
93 .fb_fillrect = cirrus_fillrect,
94 .fb_copyarea = cirrus_copyarea,
95 .fb_imageblit = cirrus_imageblit,
96 .fb_pan_display = drm_fb_helper_pan_display,
97 .fb_blank = drm_fb_helper_blank,
98 .fb_setcmap = drm_fb_helper_setcmap,
99};
100
101static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
102 struct drm_mode_fb_cmd2 *mode_cmd,
103 struct drm_gem_object **gobj_p)
104{
105 struct drm_device *dev = afbdev->helper.dev;
106 u32 bpp, depth;
107 u32 size;
108 struct drm_gem_object *gobj;
109
110 int ret = 0;
111 drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
112
113 if (bpp > 24)
114 return -EINVAL;
115 size = mode_cmd->pitches[0] * mode_cmd->height;
116 ret = cirrus_gem_create(dev, size, true, &gobj);
117 if (ret)
118 return ret;
119
120 *gobj_p = gobj;
121 return ret;
122}
123
124static int cirrusfb_create(struct cirrus_fbdev *gfbdev,
125 struct drm_fb_helper_surface_size *sizes)
126{
127 struct drm_device *dev = gfbdev->helper.dev;
128 struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
129 struct fb_info *info;
130 struct drm_framebuffer *fb;
131 struct drm_mode_fb_cmd2 mode_cmd;
132 struct device *device = &dev->pdev->dev;
133 void *sysram;
134 struct drm_gem_object *gobj = NULL;
135 struct cirrus_bo *bo = NULL;
136 int size, ret;
137
138 mode_cmd.width = sizes->surface_width;
139 mode_cmd.height = sizes->surface_height;
140 mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
141 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
142 sizes->surface_depth);
143 size = mode_cmd.pitches[0] * mode_cmd.height;
144
145 ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj);
146 if (ret) {
147 DRM_ERROR("failed to create fbcon backing object %d\n", ret);
148 return ret;
149 }
150
151 bo = gem_to_cirrus_bo(gobj);
152
153 sysram = vmalloc(size);
154 if (!sysram)
155 return -ENOMEM;
156
157 info = framebuffer_alloc(0, device);
158 if (info == NULL)
159 return -ENOMEM;
160
161 info->par = gfbdev;
162
163 ret = cirrus_framebuffer_init(cdev->dev, &gfbdev->gfb, &mode_cmd, gobj);
164 if (ret)
165 return ret;
166
167 gfbdev->sysram = sysram;
168 gfbdev->size = size;
169
170 fb = &gfbdev->gfb.base;
171 if (!fb) {
172 DRM_INFO("fb is NULL\n");
173 return -EINVAL;
174 }
175
176 /* setup helper */
177 gfbdev->helper.fb = fb;
178 gfbdev->helper.fbdev = info;
179
180 strcpy(info->fix.id, "cirrusdrmfb");
181
182
183 info->flags = FBINFO_DEFAULT;
184 info->fbops = &cirrusfb_ops;
185
186 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
187 drm_fb_helper_fill_var(info, &gfbdev->helper, sizes->fb_width,
188 sizes->fb_height);
189
190 /* setup aperture base/size for vesafb takeover */
191 info->apertures = alloc_apertures(1);
192 if (!info->apertures) {
193 ret = -ENOMEM;
194 goto out_iounmap;
195 }
196 info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base;
197 info->apertures->ranges[0].size = cdev->mc.vram_size;
198
199 info->screen_base = sysram;
200 info->screen_size = size;
201
202 info->fix.mmio_start = 0;
203 info->fix.mmio_len = 0;
204
205 ret = fb_alloc_cmap(&info->cmap, 256, 0);
206 if (ret) {
207 DRM_ERROR("%s: can't allocate color map\n", info->fix.id);
208 ret = -ENOMEM;
209 goto out_iounmap;
210 }
211
212 DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
213 DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start);
214 DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len);
215 DRM_INFO("fb depth is %d\n", fb->depth);
216 DRM_INFO(" pitch is %d\n", fb->pitches[0]);
217
218 return 0;
219out_iounmap:
220 return ret;
221}
222
223static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper,
224 struct drm_fb_helper_surface_size
225 *sizes)
226{
227 struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper;
228 int new_fb = 0;
229 int ret;
230
231 if (!helper->fb) {
232 ret = cirrusfb_create(gfbdev, sizes);
233 if (ret)
234 return ret;
235 new_fb = 1;
236 }
237 return new_fb;
238}
239
240static int cirrus_fbdev_destroy(struct drm_device *dev,
241 struct cirrus_fbdev *gfbdev)
242{
243 struct fb_info *info;
244 struct cirrus_framebuffer *gfb = &gfbdev->gfb;
245
246 if (gfbdev->helper.fbdev) {
247 info = gfbdev->helper.fbdev;
248
249 unregister_framebuffer(info);
250 if (info->cmap.len)
251 fb_dealloc_cmap(&info->cmap);
252 framebuffer_release(info);
253 }
254
255 if (gfb->obj) {
256 drm_gem_object_unreference_unlocked(gfb->obj);
257 gfb->obj = NULL;
258 }
259
260 vfree(gfbdev->sysram);
261 drm_fb_helper_fini(&gfbdev->helper);
262 drm_framebuffer_cleanup(&gfb->base);
263
264 return 0;
265}
266
267static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
268 .gamma_set = cirrus_crtc_fb_gamma_set,
269 .gamma_get = cirrus_crtc_fb_gamma_get,
270 .fb_probe = cirrus_fb_find_or_create_single,
271};
272
273int cirrus_fbdev_init(struct cirrus_device *cdev)
274{
275 struct cirrus_fbdev *gfbdev;
276 int ret;
277 int bpp_sel = 24;
278
279 /*bpp_sel = 8;*/
280 gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL);
281 if (!gfbdev)
282 return -ENOMEM;
283
284 cdev->mode_info.gfbdev = gfbdev;
285 gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
286
287 ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
288 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
289 if (ret) {
290 kfree(gfbdev);
291 return ret;
292 }
293 drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
294 drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel);
295
296 return 0;
297}
298
299void cirrus_fbdev_fini(struct cirrus_device *cdev)
300{
301 if (!cdev->mode_info.gfbdev)
302 return;
303
304 cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev);
305 kfree(cdev->mode_info.gfbdev);
306 cdev->mode_info.gfbdev = NULL;
307}