Line data Source code
1 : /*
2 : * Copyright © 2007 David Airlie
3 : *
4 : * Permission is hereby granted, free of charge, to any person obtaining a
5 : * copy of this software and associated documentation files (the "Software"),
6 : * to deal in the Software without restriction, including without limitation
7 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 : * and/or sell copies of the Software, and to permit persons to whom the
9 : * Software is furnished to do so, subject to the following conditions:
10 : *
11 : * The above copyright notice and this permission notice (including the next
12 : * paragraph) shall be included in all copies or substantial portions of the
13 : * Software.
14 : *
15 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 : * DEALINGS IN THE SOFTWARE.
22 : *
23 : * Authors:
24 : * David Airlie
25 : */
26 :
27 : #include <dev/pci/drm/drmP.h>
28 : #include <dev/pci/drm/drm_crtc.h>
29 : #include <dev/pci/drm/drm_crtc_helper.h>
30 : #include <dev/pci/drm/radeon_drm.h>
31 : #include "radeon.h"
32 :
33 : #include <dev/pci/drm/drm_fb_helper.h>
34 :
35 :
36 : /* object hierarchy -
37 : this contains a helper + a radeon fb
38 : the helper contains a pointer to radeon framebuffer baseclass.
39 : */
40 : struct radeon_fbdev {
41 : struct drm_fb_helper helper;
42 : struct radeon_framebuffer rfb;
43 : struct list_head fbdev_list;
44 : struct radeon_device *rdev;
45 : };
46 :
47 : #ifdef notyet
48 : static struct fb_ops radeonfb_ops = {
49 : .owner = THIS_MODULE,
50 : .fb_check_var = drm_fb_helper_check_var,
51 : .fb_set_par = drm_fb_helper_set_par,
52 : .fb_fillrect = drm_fb_helper_cfb_fillrect,
53 : .fb_copyarea = drm_fb_helper_cfb_copyarea,
54 : .fb_imageblit = drm_fb_helper_cfb_imageblit,
55 : .fb_pan_display = drm_fb_helper_pan_display,
56 : .fb_blank = drm_fb_helper_blank,
57 : .fb_setcmap = drm_fb_helper_setcmap,
58 : .fb_debug_enter = drm_fb_helper_debug_enter,
59 : .fb_debug_leave = drm_fb_helper_debug_leave,
60 : };
61 : #endif
62 :
63 :
64 0 : int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
65 : {
66 : int aligned = width;
67 0 : int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
68 : int pitch_mask = 0;
69 :
70 0 : switch (bpp / 8) {
71 : case 1:
72 0 : pitch_mask = align_large ? 255 : 127;
73 0 : break;
74 : case 2:
75 0 : pitch_mask = align_large ? 127 : 31;
76 0 : break;
77 : case 3:
78 : case 4:
79 0 : pitch_mask = align_large ? 63 : 15;
80 0 : break;
81 : }
82 :
83 0 : aligned += pitch_mask;
84 0 : aligned &= ~pitch_mask;
85 0 : return aligned;
86 : }
87 :
88 0 : static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
89 : {
90 0 : struct radeon_bo *rbo = gem_to_radeon_bo(gobj);
91 : int ret;
92 :
93 0 : ret = radeon_bo_reserve(rbo, false);
94 0 : if (likely(ret == 0)) {
95 0 : radeon_bo_kunmap(rbo);
96 0 : radeon_bo_unpin(rbo);
97 0 : radeon_bo_unreserve(rbo);
98 0 : }
99 0 : drm_gem_object_unreference_unlocked(gobj);
100 0 : }
101 :
102 0 : static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
103 : struct drm_mode_fb_cmd2 *mode_cmd,
104 : struct drm_gem_object **gobj_p)
105 : {
106 0 : struct radeon_device *rdev = rfbdev->rdev;
107 0 : struct drm_gem_object *gobj = NULL;
108 : struct radeon_bo *rbo = NULL;
109 : bool fb_tiled = false; /* useful for testing */
110 : u32 tiling_flags = 0;
111 : int ret;
112 : int aligned_size, size;
113 0 : int height = mode_cmd->height;
114 0 : u32 bpp, depth;
115 :
116 0 : drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
117 :
118 : /* need to align pitch with crtc limits */
119 0 : mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, bpp,
120 0 : fb_tiled) * ((bpp + 1) / 8);
121 :
122 0 : if (rdev->family >= CHIP_R600)
123 0 : height = roundup2(mode_cmd->height, 8);
124 0 : size = mode_cmd->pitches[0] * height;
125 0 : aligned_size = roundup2(size, PAGE_SIZE);
126 0 : ret = radeon_gem_object_create(rdev, aligned_size, 0,
127 : RADEON_GEM_DOMAIN_VRAM,
128 : 0, true, &gobj);
129 0 : if (ret) {
130 0 : printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
131 : aligned_size);
132 0 : return -ENOMEM;
133 : }
134 0 : rbo = gem_to_radeon_bo(gobj);
135 :
136 0 : if (fb_tiled)
137 0 : tiling_flags = RADEON_TILING_MACRO;
138 :
139 : #ifdef __BIG_ENDIAN
140 : switch (bpp) {
141 : case 32:
142 : tiling_flags |= RADEON_TILING_SWAP_32BIT;
143 : break;
144 : case 16:
145 : tiling_flags |= RADEON_TILING_SWAP_16BIT;
146 : default:
147 : break;
148 : }
149 : #endif
150 :
151 0 : if (tiling_flags) {
152 0 : ret = radeon_bo_set_tiling_flags(rbo,
153 0 : tiling_flags | RADEON_TILING_SURFACE,
154 0 : mode_cmd->pitches[0]);
155 0 : if (ret)
156 0 : dev_err(rdev->dev, "FB failed to set tiling flags\n");
157 : }
158 :
159 :
160 0 : ret = radeon_bo_reserve(rbo, false);
161 0 : if (unlikely(ret != 0))
162 : goto out_unref;
163 : /* Only 27 bit offset for legacy CRTC */
164 0 : ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
165 0 : ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
166 : NULL);
167 0 : if (ret) {
168 0 : radeon_bo_unreserve(rbo);
169 0 : goto out_unref;
170 : }
171 0 : if (fb_tiled)
172 0 : radeon_bo_check_tiling(rbo, 0, 0);
173 0 : ret = radeon_bo_kmap(rbo, NULL);
174 0 : radeon_bo_unreserve(rbo);
175 0 : if (ret) {
176 : goto out_unref;
177 : }
178 :
179 0 : *gobj_p = gobj;
180 0 : return 0;
181 : out_unref:
182 0 : radeonfb_destroy_pinned_object(gobj);
183 0 : *gobj_p = NULL;
184 0 : return ret;
185 0 : }
186 :
187 0 : static int radeonfb_create(struct drm_fb_helper *helper,
188 : struct drm_fb_helper_surface_size *sizes)
189 : {
190 : struct radeon_fbdev *rfbdev =
191 0 : container_of(helper, struct radeon_fbdev, helper);
192 0 : struct radeon_device *rdev = rfbdev->rdev;
193 : struct fb_info *info;
194 0 : struct rasops_info *ri = &rdev->ro;
195 : struct drm_framebuffer *fb = NULL;
196 0 : struct drm_mode_fb_cmd2 mode_cmd;
197 0 : struct drm_gem_object *gobj = NULL;
198 : struct radeon_bo *rbo = NULL;
199 : int ret;
200 : unsigned long tmp;
201 :
202 0 : mode_cmd.width = sizes->surface_width;
203 0 : mode_cmd.height = sizes->surface_height;
204 :
205 : /* avivo can't scanout real 24bpp */
206 0 : if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
207 0 : sizes->surface_bpp = 32;
208 :
209 0 : mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
210 0 : sizes->surface_depth);
211 :
212 0 : ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
213 0 : if (ret) {
214 0 : DRM_ERROR("failed to create fbcon object %d\n", ret);
215 0 : return ret;
216 : }
217 :
218 0 : rbo = gem_to_radeon_bo(gobj);
219 :
220 : /* okay we have an object now allocate the framebuffer */
221 0 : info = drm_fb_helper_alloc_fbi(helper);
222 0 : if (IS_ERR(info)) {
223 0 : ret = PTR_ERR(info);
224 0 : goto out_unref;
225 : }
226 :
227 0 : info->par = rfbdev;
228 :
229 0 : ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
230 0 : if (ret) {
231 0 : DRM_ERROR("failed to initialize framebuffer %d\n", ret);
232 : goto out_destroy_fbi;
233 : }
234 :
235 0 : fb = &rfbdev->rfb.base;
236 :
237 : /* setup helper */
238 0 : rfbdev->helper.fb = fb;
239 :
240 0 : memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
241 :
242 : #ifdef __linux__
243 : strcpy(info->fix.id, "radeondrmfb");
244 :
245 : drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
246 :
247 : info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
248 : info->fbops = &radeonfb_ops;
249 : #endif
250 :
251 0 : tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
252 : #ifdef __linux__
253 : info->fix.smem_start = rdev->mc.aper_base + tmp;
254 : info->fix.smem_len = radeon_bo_size(rbo);
255 : info->screen_base = rbo->kptr;
256 : info->screen_size = radeon_bo_size(rbo);
257 :
258 : drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
259 :
260 : /* setup aperture base/size for vesafb takeover */
261 : info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
262 : info->apertures->ranges[0].size = rdev->mc.aper_size;
263 : #endif
264 :
265 : /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
266 :
267 : #ifdef __linux__
268 : if (info->screen_base == NULL) {
269 : ret = -ENOSPC;
270 : goto out_destroy_fbi;
271 : }
272 :
273 : DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
274 : #endif
275 : DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base);
276 : DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
277 : DRM_INFO("fb depth is %d\n", fb->depth);
278 : DRM_INFO(" pitch is %d\n", fb->pitches[0]);
279 :
280 0 : ri->ri_bits = rbo->kptr;
281 0 : ri->ri_depth = fb->bits_per_pixel;
282 0 : ri->ri_stride = fb->pitches[0];
283 0 : ri->ri_width = sizes->fb_width;
284 0 : ri->ri_height = sizes->fb_height;
285 :
286 0 : switch (fb->pixel_format) {
287 : case DRM_FORMAT_XRGB8888:
288 0 : ri->ri_rnum = 8;
289 0 : ri->ri_rpos = 16;
290 0 : ri->ri_gnum = 8;
291 0 : ri->ri_gpos = 8;
292 0 : ri->ri_bnum = 8;
293 0 : ri->ri_bpos = 0;
294 0 : break;
295 : case DRM_FORMAT_RGB565:
296 0 : ri->ri_rnum = 5;
297 0 : ri->ri_rpos = 11;
298 0 : ri->ri_gnum = 6;
299 0 : ri->ri_gpos = 5;
300 0 : ri->ri_bnum = 5;
301 0 : ri->ri_bpos = 0;
302 0 : break;
303 : }
304 :
305 : #ifdef __linux__
306 : vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
307 : #endif
308 0 : return 0;
309 :
310 : out_destroy_fbi:
311 : #ifdef __linux__
312 : drm_fb_helper_release_fbi(helper);
313 : #endif
314 : out_unref:
315 : if (rbo) {
316 :
317 : }
318 0 : if (fb && ret) {
319 0 : drm_gem_object_unreference(gobj);
320 0 : drm_framebuffer_unregister_private(fb);
321 0 : drm_framebuffer_cleanup(fb);
322 0 : kfree(fb);
323 0 : }
324 0 : return ret;
325 0 : }
326 :
327 0 : void radeon_fb_output_poll_changed(struct radeon_device *rdev)
328 : {
329 0 : drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
330 0 : }
331 :
332 : #ifdef notyet
333 : static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
334 : {
335 : struct radeon_framebuffer *rfb = &rfbdev->rfb;
336 :
337 : drm_fb_helper_unregister_fbi(&rfbdev->helper);
338 : drm_fb_helper_release_fbi(&rfbdev->helper);
339 :
340 : if (rfb->obj) {
341 : radeonfb_destroy_pinned_object(rfb->obj);
342 : rfb->obj = NULL;
343 : }
344 : drm_fb_helper_fini(&rfbdev->helper);
345 : drm_framebuffer_unregister_private(&rfb->base);
346 : drm_framebuffer_cleanup(&rfb->base);
347 :
348 : return 0;
349 : }
350 : #endif
351 :
352 : static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
353 : .gamma_set = radeon_crtc_fb_gamma_set,
354 : .gamma_get = radeon_crtc_fb_gamma_get,
355 : .fb_probe = radeonfb_create,
356 : };
357 :
358 0 : int radeon_fbdev_init(struct radeon_device *rdev)
359 : {
360 : struct radeon_fbdev *rfbdev;
361 : int bpp_sel = 32;
362 : int ret;
363 :
364 : /* select 8 bpp console on RN50 or 16MB cards */
365 0 : if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
366 0 : bpp_sel = 8;
367 :
368 0 : rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
369 0 : if (!rfbdev)
370 0 : return -ENOMEM;
371 :
372 0 : rfbdev->rdev = rdev;
373 0 : rdev->mode_info.rfbdev = rfbdev;
374 :
375 0 : drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
376 : &radeon_fb_helper_funcs);
377 :
378 0 : ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
379 0 : rdev->num_crtc,
380 : RADEONFB_CONN_LIMIT);
381 0 : if (ret)
382 : goto free;
383 :
384 0 : ret = drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
385 0 : if (ret)
386 : goto fini;
387 :
388 : /* disable all the possible outputs/crtcs before entering KMS mode */
389 0 : drm_helper_disable_unused_functions(rdev->ddev);
390 :
391 0 : ret = drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
392 0 : if (ret)
393 : goto fini;
394 :
395 0 : return 0;
396 :
397 : fini:
398 0 : drm_fb_helper_fini(&rfbdev->helper);
399 : free:
400 0 : kfree(rfbdev);
401 0 : return ret;
402 0 : }
403 :
404 0 : void radeon_fbdev_fini(struct radeon_device *rdev)
405 : {
406 0 : if (!rdev->mode_info.rfbdev)
407 : return;
408 :
409 : #ifdef notyet
410 : radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
411 : #endif
412 0 : kfree(rdev->mode_info.rfbdev);
413 0 : rdev->mode_info.rfbdev = NULL;
414 0 : }
415 :
416 0 : void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
417 : {
418 : #ifdef __linux__
419 : fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state);
420 : #endif
421 0 : }
422 :
423 0 : bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
424 : {
425 0 : if (robj == gem_to_radeon_bo(rdev->mode_info.rfbdev->rfb.obj))
426 0 : return true;
427 0 : return false;
428 0 : }
429 :
430 0 : void radeon_fb_add_connector(struct radeon_device *rdev, struct drm_connector *connector)
431 : {
432 0 : drm_fb_helper_add_one_connector(&rdev->mode_info.rfbdev->helper, connector);
433 0 : }
434 :
435 0 : void radeon_fb_remove_connector(struct radeon_device *rdev, struct drm_connector *connector)
436 : {
437 0 : drm_fb_helper_remove_one_connector(&rdev->mode_info.rfbdev->helper, connector);
438 0 : }
439 :
440 0 : void radeon_fbdev_restore_mode(struct radeon_device *rdev)
441 : {
442 0 : struct radeon_fbdev *rfbdev = rdev->mode_info.rfbdev;
443 : struct drm_fb_helper *fb_helper;
444 : int ret;
445 :
446 0 : if (!rfbdev)
447 0 : return;
448 :
449 0 : fb_helper = &rfbdev->helper;
450 :
451 0 : ret = drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
452 : if (ret)
453 : DRM_DEBUG("failed to restore crtc mode\n");
454 0 : }
455 :
456 : void
457 0 : radeondrm_burner(void *v, u_int on, u_int flags)
458 : {
459 0 : struct rasops_info *ri = v;
460 0 : struct radeon_device *rdev = ri->ri_hw;
461 :
462 0 : task_del(systq, &rdev->burner_task);
463 :
464 0 : if (on)
465 0 : rdev->burner_fblank = FB_BLANK_UNBLANK;
466 : else {
467 0 : if (flags & WSDISPLAY_BURN_VBLANK)
468 0 : rdev->burner_fblank = FB_BLANK_VSYNC_SUSPEND;
469 : else
470 0 : rdev->burner_fblank = FB_BLANK_NORMAL;
471 : }
472 :
473 : /*
474 : * Setting the DPMS mode may sleep while waiting for vblank so
475 : * hand things off to a taskq.
476 : */
477 0 : task_add(systq, &rdev->burner_task);
478 0 : }
479 :
480 : void
481 0 : radeondrm_burner_cb(void *arg1)
482 : {
483 0 : struct radeon_device *rdev = arg1;
484 0 : struct drm_fb_helper *helper = &rdev->mode_info.rfbdev->helper;
485 :
486 0 : drm_fb_helper_blank(rdev->burner_fblank, helper->fbdev);
487 0 : }
|