diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_fb.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_fb.c | 220 |
1 files changed, 122 insertions, 98 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 8fccbf29235e..a7e4c2a89ee0 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c | |||
@@ -41,10 +41,15 @@ | |||
41 | 41 | ||
42 | #include <linux/vga_switcheroo.h> | 42 | #include <linux/vga_switcheroo.h> |
43 | 43 | ||
44 | struct radeon_fb_device { | 44 | /* object hierarchy - |
45 | this contains a helper + a radeon fb | ||
46 | the helper contains a pointer to radeon framebuffer baseclass. | ||
47 | */ | ||
48 | struct radeon_kernel_fbdev { | ||
45 | struct drm_fb_helper helper; | 49 | struct drm_fb_helper helper; |
46 | struct radeon_framebuffer *rfb; | 50 | struct radeon_framebuffer rfb; |
47 | struct radeon_device *rdev; | 51 | struct list_head fbdev_list; |
52 | struct radeon_device *rdev; | ||
48 | }; | 53 | }; |
49 | 54 | ||
50 | static struct fb_ops radeonfb_ops = { | 55 | static struct fb_ops radeonfb_ops = { |
@@ -60,45 +65,6 @@ static struct fb_ops radeonfb_ops = { | |||
60 | .fb_setcmap = drm_fb_helper_setcmap, | 65 | .fb_setcmap = drm_fb_helper_setcmap, |
61 | }; | 66 | }; |
62 | 67 | ||
63 | /** | ||
64 | * Currently it is assumed that the old framebuffer is reused. | ||
65 | * | ||
66 | * LOCKING | ||
67 | * caller should hold the mode config lock. | ||
68 | * | ||
69 | */ | ||
70 | int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc) | ||
71 | { | ||
72 | struct fb_info *info; | ||
73 | struct drm_framebuffer *fb; | ||
74 | struct drm_display_mode *mode = crtc->desired_mode; | ||
75 | |||
76 | fb = crtc->fb; | ||
77 | if (fb == NULL) { | ||
78 | return 1; | ||
79 | } | ||
80 | info = fb->fbdev; | ||
81 | if (info == NULL) { | ||
82 | return 1; | ||
83 | } | ||
84 | if (mode == NULL) { | ||
85 | return 1; | ||
86 | } | ||
87 | info->var.xres = mode->hdisplay; | ||
88 | info->var.right_margin = mode->hsync_start - mode->hdisplay; | ||
89 | info->var.hsync_len = mode->hsync_end - mode->hsync_start; | ||
90 | info->var.left_margin = mode->htotal - mode->hsync_end; | ||
91 | info->var.yres = mode->vdisplay; | ||
92 | info->var.lower_margin = mode->vsync_start - mode->vdisplay; | ||
93 | info->var.vsync_len = mode->vsync_end - mode->vsync_start; | ||
94 | info->var.upper_margin = mode->vtotal - mode->vsync_end; | ||
95 | info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100; | ||
96 | /* avoid overflow */ | ||
97 | info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | EXPORT_SYMBOL(radeonfb_resize); | ||
102 | 68 | ||
103 | static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) | 69 | static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) |
104 | { | 70 | { |
@@ -129,17 +95,14 @@ static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { | |||
129 | .gamma_get = radeon_crtc_fb_gamma_get, | 95 | .gamma_get = radeon_crtc_fb_gamma_get, |
130 | }; | 96 | }; |
131 | 97 | ||
132 | int radeonfb_create(struct drm_device *dev, | 98 | static int radeonfb_create(struct drm_device *dev, |
133 | uint32_t fb_width, uint32_t fb_height, | 99 | struct drm_fb_helper_surface_size *sizes, |
134 | uint32_t surface_width, uint32_t surface_height, | 100 | struct radeon_kernel_fbdev **rfbdev_p) |
135 | uint32_t surface_depth, uint32_t surface_bpp, | ||
136 | struct drm_framebuffer **fb_p) | ||
137 | { | 101 | { |
138 | struct radeon_device *rdev = dev->dev_private; | 102 | struct radeon_device *rdev = dev->dev_private; |
139 | struct fb_info *info; | 103 | struct fb_info *info; |
140 | struct radeon_fb_device *rfbdev; | 104 | struct radeon_kernel_fbdev *rfbdev; |
141 | struct drm_framebuffer *fb = NULL; | 105 | struct drm_framebuffer *fb = NULL; |
142 | struct radeon_framebuffer *rfb; | ||
143 | struct drm_mode_fb_cmd mode_cmd; | 106 | struct drm_mode_fb_cmd mode_cmd; |
144 | struct drm_gem_object *gobj = NULL; | 107 | struct drm_gem_object *gobj = NULL; |
145 | struct radeon_bo *rbo = NULL; | 108 | struct radeon_bo *rbo = NULL; |
@@ -151,17 +114,17 @@ int radeonfb_create(struct drm_device *dev, | |||
151 | bool fb_tiled = false; /* useful for testing */ | 114 | bool fb_tiled = false; /* useful for testing */ |
152 | u32 tiling_flags = 0; | 115 | u32 tiling_flags = 0; |
153 | 116 | ||
154 | mode_cmd.width = surface_width; | 117 | mode_cmd.width = sizes->surface_width; |
155 | mode_cmd.height = surface_height; | 118 | mode_cmd.height = sizes->surface_height; |
156 | 119 | ||
157 | /* avivo can't scanout real 24bpp */ | 120 | /* avivo can't scanout real 24bpp */ |
158 | if ((surface_bpp == 24) && ASIC_IS_AVIVO(rdev)) | 121 | if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev)) |
159 | surface_bpp = 32; | 122 | sizes->surface_bpp = 32; |
160 | 123 | ||
161 | mode_cmd.bpp = surface_bpp; | 124 | mode_cmd.bpp = sizes->surface_bpp; |
162 | /* need to align pitch with crtc limits */ | 125 | /* need to align pitch with crtc limits */ |
163 | mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); | 126 | mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); |
164 | mode_cmd.depth = surface_depth; | 127 | mode_cmd.depth = sizes->surface_depth; |
165 | 128 | ||
166 | size = mode_cmd.pitch * mode_cmd.height; | 129 | size = mode_cmd.pitch * mode_cmd.height; |
167 | aligned_size = ALIGN(size, PAGE_SIZE); | 130 | aligned_size = ALIGN(size, PAGE_SIZE); |
@@ -172,7 +135,7 @@ int radeonfb_create(struct drm_device *dev, | |||
172 | &gobj); | 135 | &gobj); |
173 | if (ret) { | 136 | if (ret) { |
174 | printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n", | 137 | printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n", |
175 | surface_width, surface_height); | 138 | sizes->surface_width, sizes->surface_height); |
176 | ret = -ENOMEM; | 139 | ret = -ENOMEM; |
177 | goto out; | 140 | goto out; |
178 | } | 141 | } |
@@ -201,12 +164,7 @@ int radeonfb_create(struct drm_device *dev, | |||
201 | dev_err(rdev->dev, "FB failed to set tiling flags\n"); | 164 | dev_err(rdev->dev, "FB failed to set tiling flags\n"); |
202 | } | 165 | } |
203 | mutex_lock(&rdev->ddev->struct_mutex); | 166 | mutex_lock(&rdev->ddev->struct_mutex); |
204 | fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj); | 167 | |
205 | if (fb == NULL) { | ||
206 | DRM_ERROR("failed to allocate fb.\n"); | ||
207 | ret = -ENOMEM; | ||
208 | goto out_unref; | ||
209 | } | ||
210 | ret = radeon_bo_reserve(rbo, false); | 168 | ret = radeon_bo_reserve(rbo, false); |
211 | if (unlikely(ret != 0)) | 169 | if (unlikely(ret != 0)) |
212 | goto out_unref; | 170 | goto out_unref; |
@@ -223,23 +181,25 @@ int radeonfb_create(struct drm_device *dev, | |||
223 | goto out_unref; | 181 | goto out_unref; |
224 | } | 182 | } |
225 | 183 | ||
226 | list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list); | 184 | info = framebuffer_alloc(sizeof(struct radeon_kernel_fbdev), device); |
227 | |||
228 | *fb_p = fb; | ||
229 | rfb = to_radeon_framebuffer(fb); | ||
230 | rdev->fbdev_rfb = rfb; | ||
231 | rdev->fbdev_rbo = rbo; | ||
232 | |||
233 | info = framebuffer_alloc(sizeof(struct radeon_fb_device), device); | ||
234 | if (info == NULL) { | 185 | if (info == NULL) { |
235 | ret = -ENOMEM; | 186 | ret = -ENOMEM; |
236 | goto out_unref; | 187 | goto out_unref; |
237 | } | 188 | } |
238 | 189 | ||
239 | rdev->fbdev_info = info; | ||
240 | rfbdev = info->par; | 190 | rfbdev = info->par; |
191 | rfbdev->rdev = rdev; | ||
192 | radeon_framebuffer_init(dev, &rfbdev->rfb, &mode_cmd, gobj); | ||
193 | fb = &rfbdev->rfb.base; | ||
194 | |||
195 | /* setup helper */ | ||
196 | rfbdev->helper.fb = fb; | ||
197 | rfbdev->helper.fbdev = info; | ||
241 | rfbdev->helper.funcs = &radeon_fb_helper_funcs; | 198 | rfbdev->helper.funcs = &radeon_fb_helper_funcs; |
242 | rfbdev->helper.dev = dev; | 199 | rfbdev->helper.dev = dev; |
200 | |||
201 | *rfbdev_p = rfbdev; | ||
202 | |||
243 | ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, rdev->num_crtc, | 203 | ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, rdev->num_crtc, |
244 | RADEONFB_CONN_LIMIT); | 204 | RADEONFB_CONN_LIMIT); |
245 | if (ret) | 205 | if (ret) |
@@ -260,7 +220,7 @@ int radeonfb_create(struct drm_device *dev, | |||
260 | info->screen_base = fbptr; | 220 | info->screen_base = fbptr; |
261 | info->screen_size = size; | 221 | info->screen_size = size; |
262 | 222 | ||
263 | drm_fb_helper_fill_var(info, fb, fb_width, fb_height); | 223 | drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height); |
264 | 224 | ||
265 | /* setup aperture base/size for vesafb takeover */ | 225 | /* setup aperture base/size for vesafb takeover */ |
266 | info->aperture_base = rdev->ddev->mode_config.fb_base; | 226 | info->aperture_base = rdev->ddev->mode_config.fb_base; |
@@ -283,9 +243,6 @@ int radeonfb_create(struct drm_device *dev, | |||
283 | DRM_INFO("fb depth is %d\n", fb->depth); | 243 | DRM_INFO("fb depth is %d\n", fb->depth); |
284 | DRM_INFO(" pitch is %d\n", fb->pitch); | 244 | DRM_INFO(" pitch is %d\n", fb->pitch); |
285 | 245 | ||
286 | fb->fbdev = info; | ||
287 | rfbdev->rfb = rfb; | ||
288 | rfbdev->rdev = rdev; | ||
289 | 246 | ||
290 | mutex_unlock(&rdev->ddev->struct_mutex); | 247 | mutex_unlock(&rdev->ddev->struct_mutex); |
291 | vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); | 248 | vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); |
@@ -300,7 +257,6 @@ out_unref: | |||
300 | } | 257 | } |
301 | } | 258 | } |
302 | if (fb && ret) { | 259 | if (fb && ret) { |
303 | list_del(&fb->filp_head); | ||
304 | drm_gem_object_unreference(gobj); | 260 | drm_gem_object_unreference(gobj); |
305 | drm_framebuffer_cleanup(fb); | 261 | drm_framebuffer_cleanup(fb); |
306 | kfree(fb); | 262 | kfree(fb); |
@@ -311,6 +267,35 @@ out: | |||
311 | return ret; | 267 | return ret; |
312 | } | 268 | } |
313 | 269 | ||
270 | static int radeon_fb_find_or_create_single(struct drm_device *dev, | ||
271 | struct drm_fb_helper_surface_size *sizes, | ||
272 | struct drm_fb_helper **fb_ptr) | ||
273 | { | ||
274 | struct radeon_device *rdev = dev->dev_private; | ||
275 | struct radeon_kernel_fbdev *rfbdev = NULL; | ||
276 | int new_fb = 0; | ||
277 | int ret; | ||
278 | |||
279 | if (!rdev->mode_info.rfbdev) { | ||
280 | ret = radeonfb_create(dev, sizes, | ||
281 | &rfbdev); | ||
282 | if (ret) | ||
283 | return ret; | ||
284 | rdev->mode_info.rfbdev = rfbdev; | ||
285 | new_fb = 1; | ||
286 | } else { | ||
287 | rfbdev = rdev->mode_info.rfbdev; | ||
288 | if (rfbdev->rfb.base.width < sizes->surface_width || | ||
289 | rfbdev->rfb.base.height < sizes->surface_height) { | ||
290 | DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | *fb_ptr = &rfbdev->helper; | ||
296 | return new_fb; | ||
297 | } | ||
298 | |||
314 | static char *mode_option; | 299 | static char *mode_option; |
315 | int radeon_parse_options(char *options) | 300 | int radeon_parse_options(char *options) |
316 | { | 301 | { |
@@ -327,7 +312,7 @@ int radeon_parse_options(char *options) | |||
327 | return 0; | 312 | return 0; |
328 | } | 313 | } |
329 | 314 | ||
330 | int radeonfb_probe(struct drm_device *dev) | 315 | static int radeonfb_probe(struct drm_device *dev) |
331 | { | 316 | { |
332 | struct radeon_device *rdev = dev->dev_private; | 317 | struct radeon_device *rdev = dev->dev_private; |
333 | int bpp_sel = 32; | 318 | int bpp_sel = 32; |
@@ -336,37 +321,76 @@ int radeonfb_probe(struct drm_device *dev) | |||
336 | if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) | 321 | if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) |
337 | bpp_sel = 8; | 322 | bpp_sel = 8; |
338 | 323 | ||
339 | return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create); | 324 | return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeon_fb_find_or_create_single); |
325 | } | ||
326 | |||
327 | void radeonfb_hotplug(struct drm_device *dev) | ||
328 | { | ||
329 | drm_helper_fb_hotplug_event(dev); | ||
330 | |||
331 | radeonfb_probe(dev); | ||
340 | } | 332 | } |
341 | 333 | ||
342 | int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) | 334 | static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_kernel_fbdev *rfbdev) |
343 | { | 335 | { |
344 | struct fb_info *info; | 336 | struct fb_info *info; |
345 | struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb); | 337 | struct radeon_framebuffer *rfb = &rfbdev->rfb; |
346 | struct radeon_bo *rbo; | 338 | struct radeon_bo *rbo; |
347 | int r; | 339 | int r; |
348 | 340 | ||
349 | if (!fb) { | 341 | rbo = rfb->obj->driver_private; |
350 | return -EINVAL; | 342 | info = rfbdev->helper.fbdev; |
351 | } | 343 | unregister_framebuffer(info); |
352 | info = fb->fbdev; | 344 | r = radeon_bo_reserve(rbo, false); |
353 | if (info) { | 345 | if (likely(r == 0)) { |
354 | struct radeon_fb_device *rfbdev = info->par; | 346 | radeon_bo_kunmap(rbo); |
355 | rbo = rfb->obj->driver_private; | 347 | radeon_bo_unpin(rbo); |
356 | unregister_framebuffer(info); | 348 | radeon_bo_unreserve(rbo); |
357 | r = radeon_bo_reserve(rbo, false); | ||
358 | if (likely(r == 0)) { | ||
359 | radeon_bo_kunmap(rbo); | ||
360 | radeon_bo_unpin(rbo); | ||
361 | radeon_bo_unreserve(rbo); | ||
362 | } | ||
363 | drm_fb_helper_free(&rfbdev->helper); | ||
364 | framebuffer_release(info); | ||
365 | } | 349 | } |
366 | 350 | ||
367 | printk(KERN_INFO "unregistered panic notifier\n"); | 351 | drm_fb_helper_free(&rfbdev->helper); |
352 | drm_framebuffer_cleanup(&rfb->base); | ||
353 | if (rfb->obj) | ||
354 | drm_gem_object_unreference_unlocked(rfb->obj); | ||
355 | |||
356 | framebuffer_release(info); | ||
368 | 357 | ||
369 | return 0; | 358 | return 0; |
370 | } | 359 | } |
371 | EXPORT_SYMBOL(radeonfb_remove); | ||
372 | MODULE_LICENSE("GPL"); | 360 | MODULE_LICENSE("GPL"); |
361 | |||
362 | int radeon_fbdev_init(struct radeon_device *rdev) | ||
363 | { | ||
364 | drm_helper_initial_config(rdev->ddev); | ||
365 | radeonfb_probe(rdev->ddev); | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | void radeon_fbdev_fini(struct radeon_device *rdev) | ||
370 | { | ||
371 | radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev); | ||
372 | rdev->mode_info.rfbdev = NULL; | ||
373 | } | ||
374 | |||
375 | void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) | ||
376 | { | ||
377 | fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state); | ||
378 | } | ||
379 | |||
380 | int radeon_fbdev_total_size(struct radeon_device *rdev) | ||
381 | { | ||
382 | struct radeon_bo *robj; | ||
383 | int size = 0; | ||
384 | |||
385 | robj = rdev->mode_info.rfbdev->rfb.obj->driver_private; | ||
386 | size += radeon_bo_size(robj); | ||
387 | return size; | ||
388 | } | ||
389 | |||
390 | bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) | ||
391 | { | ||
392 | if (robj == rdev->mode_info.rfbdev->rfb.obj->driver_private) | ||
393 | return true; | ||
394 | return false; | ||
395 | } | ||
396 | |||