linux/drivers/staging/vboxvideo/vbox_fb.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013-2017 Oracle Corporation
   3 * This file is based on ast_fb.c
   4 * Copyright 2012 Red Hat Inc.
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a
   7 * copy of this software and associated documentation files (the
   8 * "Software"), to deal in the Software without restriction, including
   9 * without limitation the rights to use, copy, modify, merge, publish,
  10 * distribute, sub license, and/or sell copies of the Software, and to
  11 * permit persons to whom the Software is furnished to do so, subject to
  12 * the following conditions:
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  19 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  20 * USE OR OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * The above copyright notice and this permission notice (including the
  23 * next paragraph) shall be included in all copies or substantial portions
  24 * of the Software.
  25 *
  26 * Authors: Dave Airlie <airlied@redhat.com>
  27 *          Michael Thayer <michael.thayer@oracle.com,
  28 */
  29#include <linux/module.h>
  30#include <linux/kernel.h>
  31#include <linux/errno.h>
  32#include <linux/string.h>
  33#include <linux/mm.h>
  34#include <linux/tty.h>
  35#include <linux/sysrq.h>
  36#include <linux/delay.h>
  37#include <linux/fb.h>
  38#include <linux/init.h>
  39
  40#include <drm/drmP.h>
  41#include <drm/drm_crtc.h>
  42#include <drm/drm_fb_helper.h>
  43#include <drm/drm_crtc_helper.h>
  44
  45#include "vbox_drv.h"
  46#include "vboxvideo.h"
  47
  48#ifdef CONFIG_DRM_KMS_FB_HELPER
  49static struct fb_deferred_io vbox_defio = {
  50        .delay = HZ / 30,
  51        .deferred_io = drm_fb_helper_deferred_io,
  52};
  53#endif
  54
  55static struct fb_ops vboxfb_ops = {
  56        .owner = THIS_MODULE,
  57        .fb_check_var = drm_fb_helper_check_var,
  58        .fb_set_par = drm_fb_helper_set_par,
  59        .fb_fillrect = drm_fb_helper_sys_fillrect,
  60        .fb_copyarea = drm_fb_helper_sys_copyarea,
  61        .fb_imageblit = drm_fb_helper_sys_imageblit,
  62        .fb_pan_display = drm_fb_helper_pan_display,
  63        .fb_blank = drm_fb_helper_blank,
  64        .fb_setcmap = drm_fb_helper_setcmap,
  65        .fb_debug_enter = drm_fb_helper_debug_enter,
  66        .fb_debug_leave = drm_fb_helper_debug_leave,
  67};
  68
  69static int vboxfb_create_object(struct vbox_fbdev *fbdev,
  70                                struct DRM_MODE_FB_CMD *mode_cmd,
  71                                struct drm_gem_object **gobj_p)
  72{
  73        struct drm_device *dev = fbdev->helper.dev;
  74        u32 size;
  75        struct drm_gem_object *gobj;
  76        u32 pitch = mode_cmd->pitches[0];
  77        int ret;
  78
  79        size = pitch * mode_cmd->height;
  80        ret = vbox_gem_create(dev, size, true, &gobj);
  81        if (ret)
  82                return ret;
  83
  84        *gobj_p = gobj;
  85
  86        return 0;
  87}
  88
  89static int vboxfb_create(struct drm_fb_helper *helper,
  90                         struct drm_fb_helper_surface_size *sizes)
  91{
  92        struct vbox_fbdev *fbdev =
  93            container_of(helper, struct vbox_fbdev, helper);
  94        struct drm_device *dev = fbdev->helper.dev;
  95        struct DRM_MODE_FB_CMD mode_cmd;
  96        struct drm_framebuffer *fb;
  97        struct fb_info *info;
  98        struct drm_gem_object *gobj;
  99        struct vbox_bo *bo;
 100        int size, ret;
 101        u32 pitch;
 102
 103        mode_cmd.width = sizes->surface_width;
 104        mode_cmd.height = sizes->surface_height;
 105        pitch = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
 106        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 107                                                          sizes->surface_depth);
 108        mode_cmd.pitches[0] = pitch;
 109
 110        size = pitch * mode_cmd.height;
 111
 112        ret = vboxfb_create_object(fbdev, &mode_cmd, &gobj);
 113        if (ret) {
 114                DRM_ERROR("failed to create fbcon backing object %d\n", ret);
 115                return ret;
 116        }
 117
 118        ret = vbox_framebuffer_init(dev, &fbdev->afb, &mode_cmd, gobj);
 119        if (ret)
 120                return ret;
 121
 122        bo = gem_to_vbox_bo(gobj);
 123
 124        ret = vbox_bo_reserve(bo, false);
 125        if (ret)
 126                return ret;
 127
 128        ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
 129        if (ret) {
 130                vbox_bo_unreserve(bo);
 131                return ret;
 132        }
 133
 134        ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
 135        vbox_bo_unreserve(bo);
 136        if (ret) {
 137                DRM_ERROR("failed to kmap fbcon\n");
 138                return ret;
 139        }
 140
 141        info = drm_fb_helper_alloc_fbi(helper);
 142        if (IS_ERR(info))
 143                return -PTR_ERR(info);
 144
 145        info->par = fbdev;
 146
 147        fbdev->size = size;
 148
 149        fb = &fbdev->afb.base;
 150        fbdev->helper.fb = fb;
 151
 152        strcpy(info->fix.id, "vboxdrmfb");
 153
 154        /*
 155         * The last flag forces a mode set on VT switches even if the kernel
 156         * does not think it is needed.
 157         */
 158        info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT |
 159                      FBINFO_MISC_ALWAYS_SETPAR;
 160        info->fbops = &vboxfb_ops;
 161
 162        /*
 163         * This seems to be done for safety checking that the framebuffer
 164         * is not registered twice by different drivers.
 165         */
 166        info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0);
 167        info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
 168
 169        drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
 170        drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width,
 171                               sizes->fb_height);
 172
 173        info->screen_base = (char __iomem *)bo->kmap.virtual;
 174        info->screen_size = size;
 175
 176#ifdef CONFIG_DRM_KMS_FB_HELPER
 177        info->fbdefio = &vbox_defio;
 178        fb_deferred_io_init(info);
 179#endif
 180
 181        info->pixmap.flags = FB_PIXMAP_SYSTEM;
 182
 183        DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height);
 184
 185        return 0;
 186}
 187
 188static struct drm_fb_helper_funcs vbox_fb_helper_funcs = {
 189        .fb_probe = vboxfb_create,
 190};
 191
 192void vbox_fbdev_fini(struct drm_device *dev)
 193{
 194        struct vbox_private *vbox = dev->dev_private;
 195        struct vbox_fbdev *fbdev = vbox->fbdev;
 196        struct vbox_framebuffer *afb = &fbdev->afb;
 197
 198#ifdef CONFIG_DRM_KMS_FB_HELPER
 199        if (fbdev->helper.fbdev && fbdev->helper.fbdev->fbdefio)
 200                fb_deferred_io_cleanup(fbdev->helper.fbdev);
 201#endif
 202
 203        drm_fb_helper_unregister_fbi(&fbdev->helper);
 204
 205        if (afb->obj) {
 206                struct vbox_bo *bo = gem_to_vbox_bo(afb->obj);
 207
 208                if (!vbox_bo_reserve(bo, false)) {
 209                        if (bo->kmap.virtual)
 210                                ttm_bo_kunmap(&bo->kmap);
 211                        /*
 212                         * QXL does this, but is it really needed before
 213                         * freeing?
 214                         */
 215                        if (bo->pin_count)
 216                                vbox_bo_unpin(bo);
 217                        vbox_bo_unreserve(bo);
 218                }
 219                drm_gem_object_put_unlocked(afb->obj);
 220                afb->obj = NULL;
 221        }
 222        drm_fb_helper_fini(&fbdev->helper);
 223
 224        drm_framebuffer_unregister_private(&afb->base);
 225        drm_framebuffer_cleanup(&afb->base);
 226}
 227
 228int vbox_fbdev_init(struct drm_device *dev)
 229{
 230        struct vbox_private *vbox = dev->dev_private;
 231        struct vbox_fbdev *fbdev;
 232        int ret;
 233
 234        fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL);
 235        if (!fbdev)
 236                return -ENOMEM;
 237
 238        vbox->fbdev = fbdev;
 239        spin_lock_init(&fbdev->dirty_lock);
 240
 241        drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs);
 242        ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs);
 243        if (ret)
 244                return ret;
 245
 246        ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
 247        if (ret)
 248                goto err_fini;
 249
 250        /* disable all the possible outputs/crtcs before entering KMS mode */
 251        drm_helper_disable_unused_functions(dev);
 252
 253        ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
 254        if (ret)
 255                goto err_fini;
 256
 257        return 0;
 258
 259err_fini:
 260        drm_fb_helper_fini(&fbdev->helper);
 261        return ret;
 262}
 263
 264void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr)
 265{
 266        struct fb_info *fbdev = vbox->fbdev->helper.fbdev;
 267
 268        fbdev->fix.smem_start = fbdev->apertures->ranges[0].base + gpu_addr;
 269        fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr;
 270}
 271