linux/drivers/gpu/drm/radeon/radeon_fb.c
<<
>>
Prefs
   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     *  Modularization
  28     */
  29
  30#include <linux/module.h>
  31#include <linux/fb.h>
  32
  33#include "drmP.h"
  34#include "drm.h"
  35#include "drm_crtc.h"
  36#include "drm_crtc_helper.h"
  37#include "radeon_drm.h"
  38#include "radeon.h"
  39
  40#include "drm_fb_helper.h"
  41
  42struct radeon_fb_device {
  43        struct drm_fb_helper helper;
  44        struct radeon_framebuffer       *rfb;
  45        struct radeon_device            *rdev;
  46};
  47
  48static 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_setcolreg = drm_fb_helper_setcolreg,
  53        .fb_fillrect = cfb_fillrect,
  54        .fb_copyarea = cfb_copyarea,
  55        .fb_imageblit = cfb_imageblit,
  56        .fb_pan_display = drm_fb_helper_pan_display,
  57        .fb_blank = drm_fb_helper_blank,
  58        .fb_setcmap = drm_fb_helper_setcmap,
  59};
  60
  61/**
  62 * Curretly it is assumed that the old framebuffer is reused.
  63 *
  64 * LOCKING
  65 * caller should hold the mode config lock.
  66 *
  67 */
  68int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
  69{
  70        struct fb_info *info;
  71        struct drm_framebuffer *fb;
  72        struct drm_display_mode *mode = crtc->desired_mode;
  73
  74        fb = crtc->fb;
  75        if (fb == NULL) {
  76                return 1;
  77        }
  78        info = fb->fbdev;
  79        if (info == NULL) {
  80                return 1;
  81        }
  82        if (mode == NULL) {
  83                return 1;
  84        }
  85        info->var.xres = mode->hdisplay;
  86        info->var.right_margin = mode->hsync_start - mode->hdisplay;
  87        info->var.hsync_len = mode->hsync_end - mode->hsync_start;
  88        info->var.left_margin = mode->htotal - mode->hsync_end;
  89        info->var.yres = mode->vdisplay;
  90        info->var.lower_margin = mode->vsync_start - mode->vdisplay;
  91        info->var.vsync_len = mode->vsync_end - mode->vsync_start;
  92        info->var.upper_margin = mode->vtotal - mode->vsync_end;
  93        info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
  94        /* avoid overflow */
  95        info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
  96
  97        return 0;
  98}
  99EXPORT_SYMBOL(radeonfb_resize);
 100
 101static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
 102{
 103        int aligned = width;
 104        int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
 105        int pitch_mask = 0;
 106
 107        switch (bpp / 8) {
 108        case 1:
 109                pitch_mask = align_large ? 255 : 127;
 110                break;
 111        case 2:
 112                pitch_mask = align_large ? 127 : 31;
 113                break;
 114        case 3:
 115        case 4:
 116                pitch_mask = align_large ? 63 : 15;
 117                break;
 118        }
 119
 120        aligned += pitch_mask;
 121        aligned &= ~pitch_mask;
 122        return aligned;
 123}
 124
 125static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
 126        .gamma_set = radeon_crtc_fb_gamma_set,
 127        .gamma_get = radeon_crtc_fb_gamma_get,
 128};
 129
 130int radeonfb_create(struct drm_device *dev,
 131                    uint32_t fb_width, uint32_t fb_height,
 132                    uint32_t surface_width, uint32_t surface_height,
 133                    uint32_t surface_depth, uint32_t surface_bpp,
 134                    struct drm_framebuffer **fb_p)
 135{
 136        struct radeon_device *rdev = dev->dev_private;
 137        struct fb_info *info;
 138        struct radeon_fb_device *rfbdev;
 139        struct drm_framebuffer *fb = NULL;
 140        struct radeon_framebuffer *rfb;
 141        struct drm_mode_fb_cmd mode_cmd;
 142        struct drm_gem_object *gobj = NULL;
 143        struct radeon_object *robj = NULL;
 144        struct device *device = &rdev->pdev->dev;
 145        int size, aligned_size, ret;
 146        u64 fb_gpuaddr;
 147        void *fbptr = NULL;
 148        unsigned long tmp;
 149        bool fb_tiled = false; /* useful for testing */
 150        u32 tiling_flags = 0;
 151        int crtc_count;
 152
 153        mode_cmd.width = surface_width;
 154        mode_cmd.height = surface_height;
 155
 156        /* avivo can't scanout real 24bpp */
 157        if ((surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
 158                surface_bpp = 32;
 159
 160        mode_cmd.bpp = surface_bpp;
 161        /* need to align pitch with crtc limits */
 162        mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8);
 163        mode_cmd.depth = surface_depth;
 164
 165        size = mode_cmd.pitch * mode_cmd.height;
 166        aligned_size = ALIGN(size, PAGE_SIZE);
 167
 168        ret = radeon_gem_object_create(rdev, aligned_size, 0,
 169                        RADEON_GEM_DOMAIN_VRAM,
 170                        false, ttm_bo_type_kernel,
 171                        false, &gobj);
 172        if (ret) {
 173                printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
 174                       surface_width, surface_height);
 175                ret = -ENOMEM;
 176                goto out;
 177        }
 178        robj = gobj->driver_private;
 179
 180        if (fb_tiled)
 181                tiling_flags = RADEON_TILING_MACRO;
 182
 183#ifdef __BIG_ENDIAN
 184        switch (mode_cmd.bpp) {
 185        case 32:
 186                tiling_flags |= RADEON_TILING_SWAP_32BIT;
 187                break;
 188        case 16:
 189                tiling_flags |= RADEON_TILING_SWAP_16BIT;
 190        default:
 191                break;
 192        }
 193#endif
 194
 195        if (tiling_flags)
 196                radeon_object_set_tiling_flags(robj, tiling_flags | RADEON_TILING_SURFACE, mode_cmd.pitch);
 197        mutex_lock(&rdev->ddev->struct_mutex);
 198        fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
 199        if (fb == NULL) {
 200                DRM_ERROR("failed to allocate fb.\n");
 201                ret = -ENOMEM;
 202                goto out_unref;
 203        }
 204        ret = radeon_object_pin(robj, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
 205        if (ret) {
 206                printk(KERN_ERR "failed to pin framebuffer\n");
 207                ret = -ENOMEM;
 208                goto out_unref;
 209        }
 210
 211        list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list);
 212
 213        *fb_p = fb;
 214        rfb = to_radeon_framebuffer(fb);
 215        rdev->fbdev_rfb = rfb;
 216        rdev->fbdev_robj = robj;
 217
 218        info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
 219        if (info == NULL) {
 220                ret = -ENOMEM;
 221                goto out_unref;
 222        }
 223
 224        rdev->fbdev_info = info;
 225        rfbdev = info->par;
 226        rfbdev->helper.funcs = &radeon_fb_helper_funcs;
 227        rfbdev->helper.dev = dev;
 228        if (rdev->flags & RADEON_SINGLE_CRTC)
 229                crtc_count = 1;
 230        else
 231                crtc_count = 2;
 232        ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, crtc_count,
 233                                            RADEONFB_CONN_LIMIT);
 234        if (ret)
 235                goto out_unref;
 236
 237        if (fb_tiled)
 238                radeon_object_check_tiling(robj, 0, 0);
 239
 240        ret = radeon_object_kmap(robj, &fbptr);
 241        if (ret) {
 242                goto out_unref;
 243        }
 244
 245        memset_io(fbptr, 0, aligned_size);
 246
 247        strcpy(info->fix.id, "radeondrmfb");
 248
 249        drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
 250
 251        info->flags = FBINFO_DEFAULT;
 252        info->fbops = &radeonfb_ops;
 253
 254        tmp = fb_gpuaddr - rdev->mc.vram_location;
 255        info->fix.smem_start = rdev->mc.aper_base + tmp;
 256        info->fix.smem_len = size;
 257        info->screen_base = fbptr;
 258        info->screen_size = size;
 259
 260        drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
 261
 262        /* setup aperture base/size for vesafb takeover */
 263        info->aperture_base = rdev->ddev->mode_config.fb_base;
 264        info->aperture_size = rdev->mc.real_vram_size;
 265
 266        info->fix.mmio_start = 0;
 267        info->fix.mmio_len = 0;
 268        info->pixmap.size = 64*1024;
 269        info->pixmap.buf_align = 8;
 270        info->pixmap.access_align = 32;
 271        info->pixmap.flags = FB_PIXMAP_SYSTEM;
 272        info->pixmap.scan_align = 1;
 273        if (info->screen_base == NULL) {
 274                ret = -ENOSPC;
 275                goto out_unref;
 276        }
 277        DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
 278        DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
 279        DRM_INFO("size %lu\n", (unsigned long)size);
 280        DRM_INFO("fb depth is %d\n", fb->depth);
 281        DRM_INFO("   pitch is %d\n", fb->pitch);
 282
 283        fb->fbdev = info;
 284        rfbdev->rfb = rfb;
 285        rfbdev->rdev = rdev;
 286
 287        mutex_unlock(&rdev->ddev->struct_mutex);
 288        return 0;
 289
 290out_unref:
 291        if (robj) {
 292                radeon_object_kunmap(robj);
 293        }
 294        if (fb && ret) {
 295                list_del(&fb->filp_head);
 296                drm_gem_object_unreference(gobj);
 297                drm_framebuffer_cleanup(fb);
 298                kfree(fb);
 299        }
 300        drm_gem_object_unreference(gobj);
 301        mutex_unlock(&rdev->ddev->struct_mutex);
 302out:
 303        return ret;
 304}
 305
 306static char *mode_option;
 307int radeon_parse_options(char *options)
 308{
 309        char *this_opt;
 310
 311        if (!options || !*options)
 312                return 0;
 313
 314        while ((this_opt = strsep(&options, ",")) != NULL) {
 315                if (!*this_opt)
 316                        continue;
 317                mode_option = this_opt;
 318        }
 319        return 0;
 320}
 321
 322int radeonfb_probe(struct drm_device *dev)
 323{
 324        return drm_fb_helper_single_fb_probe(dev, 32, &radeonfb_create);
 325}
 326
 327int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
 328{
 329        struct fb_info *info;
 330        struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
 331        struct radeon_object *robj;
 332
 333        if (!fb) {
 334                return -EINVAL;
 335        }
 336        info = fb->fbdev;
 337        if (info) {
 338                struct radeon_fb_device *rfbdev = info->par;
 339                robj = rfb->obj->driver_private;
 340                unregister_framebuffer(info);
 341                radeon_object_kunmap(robj);
 342                radeon_object_unpin(robj);
 343                drm_fb_helper_free(&rfbdev->helper);
 344                framebuffer_release(info);
 345        }
 346
 347        printk(KERN_INFO "unregistered panic notifier\n");
 348
 349        return 0;
 350}
 351EXPORT_SYMBOL(radeonfb_remove);
 352MODULE_LICENSE("GPL");
 353
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.