diff options
Diffstat (limited to 'drivers/gpu/drm/bochs/bochs_fbdev.c')
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_fbdev.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c new file mode 100644 index 000000000000..4da5206b7cc9 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | */ | ||
7 | |||
8 | #include "bochs.h" | ||
9 | |||
10 | /* ---------------------------------------------------------------------- */ | ||
11 | |||
12 | static struct fb_ops bochsfb_ops = { | ||
13 | .owner = THIS_MODULE, | ||
14 | .fb_check_var = drm_fb_helper_check_var, | ||
15 | .fb_set_par = drm_fb_helper_set_par, | ||
16 | .fb_fillrect = sys_fillrect, | ||
17 | .fb_copyarea = sys_copyarea, | ||
18 | .fb_imageblit = sys_imageblit, | ||
19 | .fb_pan_display = drm_fb_helper_pan_display, | ||
20 | .fb_blank = drm_fb_helper_blank, | ||
21 | .fb_setcmap = drm_fb_helper_setcmap, | ||
22 | }; | ||
23 | |||
24 | static int bochsfb_create_object(struct bochs_device *bochs, | ||
25 | struct drm_mode_fb_cmd2 *mode_cmd, | ||
26 | struct drm_gem_object **gobj_p) | ||
27 | { | ||
28 | struct drm_device *dev = bochs->dev; | ||
29 | struct drm_gem_object *gobj; | ||
30 | u32 size; | ||
31 | int ret = 0; | ||
32 | |||
33 | size = mode_cmd->pitches[0] * mode_cmd->height; | ||
34 | ret = bochs_gem_create(dev, size, true, &gobj); | ||
35 | if (ret) | ||
36 | return ret; | ||
37 | |||
38 | *gobj_p = gobj; | ||
39 | return ret; | ||
40 | } | ||
41 | |||
42 | static int bochsfb_create(struct drm_fb_helper *helper, | ||
43 | struct drm_fb_helper_surface_size *sizes) | ||
44 | { | ||
45 | struct bochs_device *bochs = | ||
46 | container_of(helper, struct bochs_device, fb.helper); | ||
47 | struct drm_device *dev = bochs->dev; | ||
48 | struct fb_info *info; | ||
49 | struct drm_framebuffer *fb; | ||
50 | struct drm_mode_fb_cmd2 mode_cmd; | ||
51 | struct device *device = &dev->pdev->dev; | ||
52 | struct drm_gem_object *gobj = NULL; | ||
53 | struct bochs_bo *bo = NULL; | ||
54 | int size, ret; | ||
55 | |||
56 | if (sizes->surface_bpp != 32) | ||
57 | return -EINVAL; | ||
58 | |||
59 | mode_cmd.width = sizes->surface_width; | ||
60 | mode_cmd.height = sizes->surface_height; | ||
61 | mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); | ||
62 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | ||
63 | sizes->surface_depth); | ||
64 | size = mode_cmd.pitches[0] * mode_cmd.height; | ||
65 | |||
66 | /* alloc, pin & map bo */ | ||
67 | ret = bochsfb_create_object(bochs, &mode_cmd, &gobj); | ||
68 | if (ret) { | ||
69 | DRM_ERROR("failed to create fbcon backing object %d\n", ret); | ||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | bo = gem_to_bochs_bo(gobj); | ||
74 | |||
75 | ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); | ||
76 | if (ret) | ||
77 | return ret; | ||
78 | |||
79 | ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL); | ||
80 | if (ret) { | ||
81 | DRM_ERROR("failed to pin fbcon\n"); | ||
82 | ttm_bo_unreserve(&bo->bo); | ||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, | ||
87 | &bo->kmap); | ||
88 | if (ret) { | ||
89 | DRM_ERROR("failed to kmap fbcon\n"); | ||
90 | ttm_bo_unreserve(&bo->bo); | ||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | ttm_bo_unreserve(&bo->bo); | ||
95 | |||
96 | /* init fb device */ | ||
97 | info = framebuffer_alloc(0, device); | ||
98 | if (info == NULL) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | info->par = &bochs->fb.helper; | ||
102 | |||
103 | ret = bochs_framebuffer_init(bochs->dev, &bochs->fb.gfb, &mode_cmd, gobj); | ||
104 | if (ret) | ||
105 | return ret; | ||
106 | |||
107 | bochs->fb.size = size; | ||
108 | |||
109 | /* setup helper */ | ||
110 | fb = &bochs->fb.gfb.base; | ||
111 | bochs->fb.helper.fb = fb; | ||
112 | bochs->fb.helper.fbdev = info; | ||
113 | |||
114 | strcpy(info->fix.id, "bochsdrmfb"); | ||
115 | |||
116 | info->flags = FBINFO_DEFAULT; | ||
117 | info->fbops = &bochsfb_ops; | ||
118 | |||
119 | drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); | ||
120 | drm_fb_helper_fill_var(info, &bochs->fb.helper, sizes->fb_width, | ||
121 | sizes->fb_height); | ||
122 | |||
123 | info->screen_base = bo->kmap.virtual; | ||
124 | info->screen_size = size; | ||
125 | |||
126 | #if 0 | ||
127 | /* FIXME: get this right for mmap(/dev/fb0) */ | ||
128 | info->fix.smem_start = bochs_bo_mmap_offset(bo); | ||
129 | info->fix.smem_len = size; | ||
130 | #endif | ||
131 | |||
132 | ret = fb_alloc_cmap(&info->cmap, 256, 0); | ||
133 | if (ret) { | ||
134 | DRM_ERROR("%s: can't allocate color map\n", info->fix.id); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int bochs_fbdev_destroy(struct bochs_device *bochs) | ||
142 | { | ||
143 | struct bochs_framebuffer *gfb = &bochs->fb.gfb; | ||
144 | struct fb_info *info; | ||
145 | |||
146 | DRM_DEBUG_DRIVER("\n"); | ||
147 | |||
148 | if (bochs->fb.helper.fbdev) { | ||
149 | info = bochs->fb.helper.fbdev; | ||
150 | |||
151 | unregister_framebuffer(info); | ||
152 | if (info->cmap.len) | ||
153 | fb_dealloc_cmap(&info->cmap); | ||
154 | framebuffer_release(info); | ||
155 | } | ||
156 | |||
157 | if (gfb->obj) { | ||
158 | drm_gem_object_unreference_unlocked(gfb->obj); | ||
159 | gfb->obj = NULL; | ||
160 | } | ||
161 | |||
162 | drm_fb_helper_fini(&bochs->fb.helper); | ||
163 | drm_framebuffer_unregister_private(&gfb->base); | ||
164 | drm_framebuffer_cleanup(&gfb->base); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | void bochs_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, | ||
170 | u16 blue, int regno) | ||
171 | { | ||
172 | } | ||
173 | |||
174 | void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, | ||
175 | u16 *blue, int regno) | ||
176 | { | ||
177 | *red = regno; | ||
178 | *green = regno; | ||
179 | *blue = regno; | ||
180 | } | ||
181 | |||
182 | static struct drm_fb_helper_funcs bochs_fb_helper_funcs = { | ||
183 | .gamma_set = bochs_fb_gamma_set, | ||
184 | .gamma_get = bochs_fb_gamma_get, | ||
185 | .fb_probe = bochsfb_create, | ||
186 | }; | ||
187 | |||
188 | int bochs_fbdev_init(struct bochs_device *bochs) | ||
189 | { | ||
190 | int ret; | ||
191 | |||
192 | bochs->fb.helper.funcs = &bochs_fb_helper_funcs; | ||
193 | spin_lock_init(&bochs->fb.dirty_lock); | ||
194 | |||
195 | ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, | ||
196 | 1, 1); | ||
197 | if (ret) | ||
198 | return ret; | ||
199 | |||
200 | drm_fb_helper_single_add_all_connectors(&bochs->fb.helper); | ||
201 | drm_helper_disable_unused_functions(bochs->dev); | ||
202 | drm_fb_helper_initial_config(&bochs->fb.helper, 32); | ||
203 | |||
204 | bochs->fb.initialized = true; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | void bochs_fbdev_fini(struct bochs_device *bochs) | ||
209 | { | ||
210 | if (!bochs->fb.initialized) | ||
211 | return; | ||
212 | |||
213 | bochs_fbdev_destroy(bochs); | ||
214 | bochs->fb.initialized = false; | ||
215 | } | ||