Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright © 2007 David Airlie
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice (including the next
0012  * paragraph) shall be included in all copies or substantial portions of the
0013  * Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0018  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0020  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
0021  * DEALINGS IN THE SOFTWARE.
0022  *
0023  * Authors:
0024  *     David Airlie
0025  */
0026 
0027 #include <linux/module.h>
0028 #include <linux/pci.h>
0029 #include <linux/pm_runtime.h>
0030 #include <linux/slab.h>
0031 #include <linux/vga_switcheroo.h>
0032 
0033 #include <drm/drm_crtc.h>
0034 #include <drm/drm_crtc_helper.h>
0035 #include <drm/drm_fb_helper.h>
0036 #include <drm/drm_fourcc.h>
0037 #include <drm/drm_framebuffer.h>
0038 #include <drm/radeon_drm.h>
0039 
0040 #include "radeon.h"
0041 
0042 /* object hierarchy -
0043  * this contains a helper + a radeon fb
0044  * the helper contains a pointer to radeon framebuffer baseclass.
0045  */
0046 struct radeon_fbdev {
0047     struct drm_fb_helper helper; /* must be first */
0048     struct drm_framebuffer fb;
0049     struct radeon_device *rdev;
0050 };
0051 
0052 static int
0053 radeonfb_open(struct fb_info *info, int user)
0054 {
0055     struct radeon_fbdev *rfbdev = info->par;
0056     struct radeon_device *rdev = rfbdev->rdev;
0057     int ret = pm_runtime_get_sync(rdev->ddev->dev);
0058 
0059     if (ret < 0 && ret != -EACCES) {
0060         pm_runtime_mark_last_busy(rdev->ddev->dev);
0061         pm_runtime_put_autosuspend(rdev->ddev->dev);
0062         return ret;
0063     }
0064     return 0;
0065 }
0066 
0067 static int
0068 radeonfb_release(struct fb_info *info, int user)
0069 {
0070     struct radeon_fbdev *rfbdev = info->par;
0071     struct radeon_device *rdev = rfbdev->rdev;
0072 
0073     pm_runtime_mark_last_busy(rdev->ddev->dev);
0074     pm_runtime_put_autosuspend(rdev->ddev->dev);
0075     return 0;
0076 }
0077 
0078 static const struct fb_ops radeonfb_ops = {
0079     .owner = THIS_MODULE,
0080     DRM_FB_HELPER_DEFAULT_OPS,
0081     .fb_open = radeonfb_open,
0082     .fb_release = radeonfb_release,
0083     .fb_fillrect = drm_fb_helper_cfb_fillrect,
0084     .fb_copyarea = drm_fb_helper_cfb_copyarea,
0085     .fb_imageblit = drm_fb_helper_cfb_imageblit,
0086 };
0087 
0088 
0089 int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tiled)
0090 {
0091     int aligned = width;
0092     int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
0093     int pitch_mask = 0;
0094 
0095     switch (cpp) {
0096     case 1:
0097         pitch_mask = align_large ? 255 : 127;
0098         break;
0099     case 2:
0100         pitch_mask = align_large ? 127 : 31;
0101         break;
0102     case 3:
0103     case 4:
0104         pitch_mask = align_large ? 63 : 15;
0105         break;
0106     }
0107 
0108     aligned += pitch_mask;
0109     aligned &= ~pitch_mask;
0110     return aligned * cpp;
0111 }
0112 
0113 static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
0114 {
0115     struct radeon_bo *rbo = gem_to_radeon_bo(gobj);
0116     int ret;
0117 
0118     ret = radeon_bo_reserve(rbo, false);
0119     if (likely(ret == 0)) {
0120         radeon_bo_kunmap(rbo);
0121         radeon_bo_unpin(rbo);
0122         radeon_bo_unreserve(rbo);
0123     }
0124     drm_gem_object_put(gobj);
0125 }
0126 
0127 static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
0128                      struct drm_mode_fb_cmd2 *mode_cmd,
0129                      struct drm_gem_object **gobj_p)
0130 {
0131     const struct drm_format_info *info;
0132     struct radeon_device *rdev = rfbdev->rdev;
0133     struct drm_gem_object *gobj = NULL;
0134     struct radeon_bo *rbo = NULL;
0135     bool fb_tiled = false; /* useful for testing */
0136     u32 tiling_flags = 0;
0137     int ret;
0138     int aligned_size, size;
0139     int height = mode_cmd->height;
0140     u32 cpp;
0141 
0142     info = drm_get_format_info(rdev->ddev, mode_cmd);
0143     cpp = info->cpp[0];
0144 
0145     /* need to align pitch with crtc limits */
0146     mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, cpp,
0147                           fb_tiled);
0148 
0149     if (rdev->family >= CHIP_R600)
0150         height = ALIGN(mode_cmd->height, 8);
0151     size = mode_cmd->pitches[0] * height;
0152     aligned_size = ALIGN(size, PAGE_SIZE);
0153     ret = radeon_gem_object_create(rdev, aligned_size, 0,
0154                        RADEON_GEM_DOMAIN_VRAM,
0155                        0, true, &gobj);
0156     if (ret) {
0157         pr_err("failed to allocate framebuffer (%d)\n", aligned_size);
0158         return -ENOMEM;
0159     }
0160     rbo = gem_to_radeon_bo(gobj);
0161 
0162     if (fb_tiled)
0163         tiling_flags = RADEON_TILING_MACRO;
0164 
0165 #ifdef __BIG_ENDIAN
0166     switch (cpp) {
0167     case 4:
0168         tiling_flags |= RADEON_TILING_SWAP_32BIT;
0169         break;
0170     case 2:
0171         tiling_flags |= RADEON_TILING_SWAP_16BIT;
0172         break;
0173     default:
0174         break;
0175     }
0176 #endif
0177 
0178     if (tiling_flags) {
0179         ret = radeon_bo_set_tiling_flags(rbo,
0180                          tiling_flags | RADEON_TILING_SURFACE,
0181                          mode_cmd->pitches[0]);
0182         if (ret)
0183             dev_err(rdev->dev, "FB failed to set tiling flags\n");
0184     }
0185 
0186 
0187     ret = radeon_bo_reserve(rbo, false);
0188     if (unlikely(ret != 0))
0189         goto out_unref;
0190     /* Only 27 bit offset for legacy CRTC */
0191     ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
0192                        ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
0193                        NULL);
0194     if (ret) {
0195         radeon_bo_unreserve(rbo);
0196         goto out_unref;
0197     }
0198     if (fb_tiled)
0199         radeon_bo_check_tiling(rbo, 0, 0);
0200     ret = radeon_bo_kmap(rbo, NULL);
0201     radeon_bo_unreserve(rbo);
0202     if (ret)
0203         goto out_unref;
0204 
0205     *gobj_p = gobj;
0206     return 0;
0207 out_unref:
0208     radeonfb_destroy_pinned_object(gobj);
0209     *gobj_p = NULL;
0210     return ret;
0211 }
0212 
0213 static int radeonfb_create(struct drm_fb_helper *helper,
0214                struct drm_fb_helper_surface_size *sizes)
0215 {
0216     struct radeon_fbdev *rfbdev =
0217         container_of(helper, struct radeon_fbdev, helper);
0218     struct radeon_device *rdev = rfbdev->rdev;
0219     struct fb_info *info;
0220     struct drm_framebuffer *fb = NULL;
0221     struct drm_mode_fb_cmd2 mode_cmd;
0222     struct drm_gem_object *gobj = NULL;
0223     struct radeon_bo *rbo = NULL;
0224     int ret;
0225     unsigned long tmp;
0226 
0227     mode_cmd.width = sizes->surface_width;
0228     mode_cmd.height = sizes->surface_height;
0229 
0230     /* avivo can't scanout real 24bpp */
0231     if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
0232         sizes->surface_bpp = 32;
0233 
0234     mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
0235                               sizes->surface_depth);
0236 
0237     ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
0238     if (ret) {
0239         DRM_ERROR("failed to create fbcon object %d\n", ret);
0240         return ret;
0241     }
0242 
0243     rbo = gem_to_radeon_bo(gobj);
0244 
0245     /* okay we have an object now allocate the framebuffer */
0246     info = drm_fb_helper_alloc_fbi(helper);
0247     if (IS_ERR(info)) {
0248         ret = PTR_ERR(info);
0249         goto out;
0250     }
0251 
0252     /* radeon resume is fragile and needs a vt switch to help it along */
0253     info->skip_vt_switch = false;
0254 
0255     ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->fb, &mode_cmd, gobj);
0256     if (ret) {
0257         DRM_ERROR("failed to initialize framebuffer %d\n", ret);
0258         goto out;
0259     }
0260 
0261     fb = &rfbdev->fb;
0262 
0263     /* setup helper */
0264     rfbdev->helper.fb = fb;
0265 
0266     memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
0267 
0268     info->fbops = &radeonfb_ops;
0269 
0270     tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
0271     info->fix.smem_start = rdev->mc.aper_base + tmp;
0272     info->fix.smem_len = radeon_bo_size(rbo);
0273     info->screen_base = rbo->kptr;
0274     info->screen_size = radeon_bo_size(rbo);
0275 
0276     drm_fb_helper_fill_info(info, &rfbdev->helper, sizes);
0277 
0278     /* setup aperture base/size for vesafb takeover */
0279     info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
0280     info->apertures->ranges[0].size = rdev->mc.aper_size;
0281 
0282     /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
0283 
0284     if (info->screen_base == NULL) {
0285         ret = -ENOSPC;
0286         goto out;
0287     }
0288 
0289     DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
0290     DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
0291     DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
0292     DRM_INFO("fb depth is %d\n", fb->format->depth);
0293     DRM_INFO("   pitch is %d\n", fb->pitches[0]);
0294 
0295     vga_switcheroo_client_fb_set(rdev->pdev, info);
0296     return 0;
0297 
0298 out:
0299     if (fb && ret) {
0300         drm_gem_object_put(gobj);
0301         drm_framebuffer_unregister_private(fb);
0302         drm_framebuffer_cleanup(fb);
0303         kfree(fb);
0304     }
0305     return ret;
0306 }
0307 
0308 static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
0309 {
0310     struct drm_framebuffer *fb = &rfbdev->fb;
0311 
0312     drm_fb_helper_unregister_fbi(&rfbdev->helper);
0313 
0314     if (fb->obj[0]) {
0315         radeonfb_destroy_pinned_object(fb->obj[0]);
0316         fb->obj[0] = NULL;
0317         drm_framebuffer_unregister_private(fb);
0318         drm_framebuffer_cleanup(fb);
0319     }
0320     drm_fb_helper_fini(&rfbdev->helper);
0321 
0322     return 0;
0323 }
0324 
0325 static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
0326     .fb_probe = radeonfb_create,
0327 };
0328 
0329 int radeon_fbdev_init(struct radeon_device *rdev)
0330 {
0331     struct radeon_fbdev *rfbdev;
0332     int bpp_sel = 32;
0333     int ret;
0334 
0335     /* don't enable fbdev if no connectors */
0336     if (list_empty(&rdev->ddev->mode_config.connector_list))
0337         return 0;
0338 
0339     /* select 8 bpp console on 8MB cards, or 16 bpp on RN50 or 32MB */
0340     if (rdev->mc.real_vram_size <= (8*1024*1024))
0341         bpp_sel = 8;
0342     else if (ASIC_IS_RN50(rdev) ||
0343          rdev->mc.real_vram_size <= (32*1024*1024))
0344         bpp_sel = 16;
0345 
0346     rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
0347     if (!rfbdev)
0348         return -ENOMEM;
0349 
0350     rfbdev->rdev = rdev;
0351     rdev->mode_info.rfbdev = rfbdev;
0352 
0353     drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
0354                   &radeon_fb_helper_funcs);
0355 
0356     ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper);
0357     if (ret)
0358         goto free;
0359 
0360     /* disable all the possible outputs/crtcs before entering KMS mode */
0361     drm_helper_disable_unused_functions(rdev->ddev);
0362 
0363     ret = drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
0364     if (ret)
0365         goto fini;
0366 
0367     return 0;
0368 
0369 fini:
0370     drm_fb_helper_fini(&rfbdev->helper);
0371 free:
0372     kfree(rfbdev);
0373     return ret;
0374 }
0375 
0376 void radeon_fbdev_fini(struct radeon_device *rdev)
0377 {
0378     if (!rdev->mode_info.rfbdev)
0379         return;
0380 
0381     radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
0382     kfree(rdev->mode_info.rfbdev);
0383     rdev->mode_info.rfbdev = NULL;
0384 }
0385 
0386 void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
0387 {
0388     if (rdev->mode_info.rfbdev)
0389         drm_fb_helper_set_suspend(&rdev->mode_info.rfbdev->helper, state);
0390 }
0391 
0392 bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
0393 {
0394     if (!rdev->mode_info.rfbdev)
0395         return false;
0396 
0397     if (robj == gem_to_radeon_bo(rdev->mode_info.rfbdev->fb.obj[0]))
0398         return true;
0399     return false;
0400 }