linux/drivers/gpu/drm/amd/amdgpu/amdgpu_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#include <linux/module.h>
  27#include <linux/slab.h>
  28#include <linux/pm_runtime.h>
  29
  30#include <drm/drmP.h>
  31#include <drm/drm_crtc.h>
  32#include <drm/drm_crtc_helper.h>
  33#include <drm/amdgpu_drm.h>
  34#include "amdgpu.h"
  35#include "cikd.h"
  36
  37#include <drm/drm_fb_helper.h>
  38
  39#include <linux/vga_switcheroo.h>
  40
  41/* object hierarchy -
  42   this contains a helper + a amdgpu fb
  43   the helper contains a pointer to amdgpu framebuffer baseclass.
  44*/
  45struct amdgpu_fbdev {
  46        struct drm_fb_helper helper;
  47        struct amdgpu_framebuffer rfb;
  48        struct amdgpu_device *adev;
  49};
  50
  51static int
  52amdgpufb_open(struct fb_info *info, int user)
  53{
  54        struct amdgpu_fbdev *rfbdev = info->par;
  55        struct amdgpu_device *adev = rfbdev->adev;
  56        int ret = pm_runtime_get_sync(adev->ddev->dev);
  57        if (ret < 0 && ret != -EACCES) {
  58                pm_runtime_mark_last_busy(adev->ddev->dev);
  59                pm_runtime_put_autosuspend(adev->ddev->dev);
  60                return ret;
  61        }
  62        return 0;
  63}
  64
  65static int
  66amdgpufb_release(struct fb_info *info, int user)
  67{
  68        struct amdgpu_fbdev *rfbdev = info->par;
  69        struct amdgpu_device *adev = rfbdev->adev;
  70
  71        pm_runtime_mark_last_busy(adev->ddev->dev);
  72        pm_runtime_put_autosuspend(adev->ddev->dev);
  73        return 0;
  74}
  75
  76static struct fb_ops amdgpufb_ops = {
  77        .owner = THIS_MODULE,
  78        .fb_open = amdgpufb_open,
  79        .fb_release = amdgpufb_release,
  80        .fb_check_var = drm_fb_helper_check_var,
  81        .fb_set_par = drm_fb_helper_set_par,
  82        .fb_fillrect = drm_fb_helper_cfb_fillrect,
  83        .fb_copyarea = drm_fb_helper_cfb_copyarea,
  84        .fb_imageblit = drm_fb_helper_cfb_imageblit,
  85        .fb_pan_display = drm_fb_helper_pan_display,
  86        .fb_blank = drm_fb_helper_blank,
  87        .fb_setcmap = drm_fb_helper_setcmap,
  88        .fb_debug_enter = drm_fb_helper_debug_enter,
  89        .fb_debug_leave = drm_fb_helper_debug_leave,
  90};
  91
  92
  93int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tiled)
  94{
  95        int aligned = width;
  96        int pitch_mask = 0;
  97
  98        switch (bpp / 8) {
  99        case 1:
 100                pitch_mask = 255;
 101                break;
 102        case 2:
 103                pitch_mask = 127;
 104                break;
 105        case 3:
 106        case 4:
 107                pitch_mask = 63;
 108                break;
 109        }
 110
 111        aligned += pitch_mask;
 112        aligned &= ~pitch_mask;
 113        return aligned;
 114}
 115
 116static void amdgpufb_destroy_pinned_object(struct drm_gem_object *gobj)
 117{
 118        struct amdgpu_bo *abo = gem_to_amdgpu_bo(gobj);
 119        int ret;
 120
 121        ret = amdgpu_bo_reserve(abo, false);
 122        if (likely(ret == 0)) {
 123                amdgpu_bo_kunmap(abo);
 124                amdgpu_bo_unpin(abo);
 125                amdgpu_bo_unreserve(abo);
 126        }
 127        drm_gem_object_unreference_unlocked(gobj);
 128}
 129
 130static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev,
 131                                         struct drm_mode_fb_cmd2 *mode_cmd,
 132                                         struct drm_gem_object **gobj_p)
 133{
 134        struct amdgpu_device *adev = rfbdev->adev;
 135        struct drm_gem_object *gobj = NULL;
 136        struct amdgpu_bo *abo = NULL;
 137        bool fb_tiled = false; /* useful for testing */
 138        u32 tiling_flags = 0;
 139        int ret;
 140        int aligned_size, size;
 141        int height = mode_cmd->height;
 142        u32 bpp, depth;
 143
 144        drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
 145
 146        /* need to align pitch with crtc limits */
 147        mode_cmd->pitches[0] = amdgpu_align_pitch(adev, mode_cmd->width, bpp,
 148                                                  fb_tiled) * ((bpp + 1) / 8);
 149
 150        height = ALIGN(mode_cmd->height, 8);
 151        size = mode_cmd->pitches[0] * height;
 152        aligned_size = ALIGN(size, PAGE_SIZE);
 153        ret = amdgpu_gem_object_create(adev, aligned_size, 0,
 154                                       AMDGPU_GEM_DOMAIN_VRAM,
 155                                       AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
 156                                       true, &gobj);
 157        if (ret) {
 158                printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
 159                       aligned_size);
 160                return -ENOMEM;
 161        }
 162        abo = gem_to_amdgpu_bo(gobj);
 163
 164        if (fb_tiled)
 165                tiling_flags = AMDGPU_TILING_SET(ARRAY_MODE, GRPH_ARRAY_2D_TILED_THIN1);
 166
 167        ret = amdgpu_bo_reserve(abo, false);
 168        if (unlikely(ret != 0))
 169                goto out_unref;
 170
 171        if (tiling_flags) {
 172                ret = amdgpu_bo_set_tiling_flags(abo,
 173                                                 tiling_flags);
 174                if (ret)
 175                        dev_err(adev->dev, "FB failed to set tiling flags\n");
 176        }
 177
 178
 179        ret = amdgpu_bo_pin_restricted(abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, NULL);
 180        if (ret) {
 181                amdgpu_bo_unreserve(abo);
 182                goto out_unref;
 183        }
 184        ret = amdgpu_bo_kmap(abo, NULL);
 185        amdgpu_bo_unreserve(abo);
 186        if (ret) {
 187                goto out_unref;
 188        }
 189
 190        *gobj_p = gobj;
 191        return 0;
 192out_unref:
 193        amdgpufb_destroy_pinned_object(gobj);
 194        *gobj_p = NULL;
 195        return ret;
 196}
 197
 198static int amdgpufb_create(struct drm_fb_helper *helper,
 199                           struct drm_fb_helper_surface_size *sizes)
 200{
 201        struct amdgpu_fbdev *rfbdev = (struct amdgpu_fbdev *)helper;
 202        struct amdgpu_device *adev = rfbdev->adev;
 203        struct fb_info *info;
 204        struct drm_framebuffer *fb = NULL;
 205        struct drm_mode_fb_cmd2 mode_cmd;
 206        struct drm_gem_object *gobj = NULL;
 207        struct amdgpu_bo *abo = NULL;
 208        int ret;
 209        unsigned long tmp;
 210
 211        mode_cmd.width = sizes->surface_width;
 212        mode_cmd.height = sizes->surface_height;
 213
 214        if (sizes->surface_bpp == 24)
 215                sizes->surface_bpp = 32;
 216
 217        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 218                                                          sizes->surface_depth);
 219
 220        ret = amdgpufb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
 221        if (ret) {
 222                DRM_ERROR("failed to create fbcon object %d\n", ret);
 223                return ret;
 224        }
 225
 226        abo = gem_to_amdgpu_bo(gobj);
 227
 228        /* okay we have an object now allocate the framebuffer */
 229        info = drm_fb_helper_alloc_fbi(helper);
 230        if (IS_ERR(info)) {
 231                ret = PTR_ERR(info);
 232                goto out_unref;
 233        }
 234
 235        info->par = rfbdev;
 236        info->skip_vt_switch = true;
 237
 238        ret = amdgpu_framebuffer_init(adev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
 239        if (ret) {
 240                DRM_ERROR("failed to initialize framebuffer %d\n", ret);
 241                goto out_destroy_fbi;
 242        }
 243
 244        fb = &rfbdev->rfb.base;
 245
 246        /* setup helper */
 247        rfbdev->helper.fb = fb;
 248
 249        memset_io(abo->kptr, 0x0, amdgpu_bo_size(abo));
 250
 251        strcpy(info->fix.id, "amdgpudrmfb");
 252
 253        drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
 254
 255        info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
 256        info->fbops = &amdgpufb_ops;
 257
 258        tmp = amdgpu_bo_gpu_offset(abo) - adev->mc.vram_start;
 259        info->fix.smem_start = adev->mc.aper_base + tmp;
 260        info->fix.smem_len = amdgpu_bo_size(abo);
 261        info->screen_base = abo->kptr;
 262        info->screen_size = amdgpu_bo_size(abo);
 263
 264        drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
 265
 266        /* setup aperture base/size for vesafb takeover */
 267        info->apertures->ranges[0].base = adev->ddev->mode_config.fb_base;
 268        info->apertures->ranges[0].size = adev->mc.aper_size;
 269
 270        /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
 271
 272        if (info->screen_base == NULL) {
 273                ret = -ENOSPC;
 274                goto out_destroy_fbi;
 275        }
 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)adev->mc.aper_base);
 279        DRM_INFO("size %lu\n", (unsigned long)amdgpu_bo_size(abo));
 280        DRM_INFO("fb depth is %d\n", fb->depth);
 281        DRM_INFO("   pitch is %d\n", fb->pitches[0]);
 282
 283        vga_switcheroo_client_fb_set(adev->ddev->pdev, info);
 284        return 0;
 285
 286out_destroy_fbi:
 287        drm_fb_helper_release_fbi(helper);
 288out_unref:
 289        if (abo) {
 290
 291        }
 292        if (fb && ret) {
 293                drm_gem_object_unreference_unlocked(gobj);
 294                drm_framebuffer_unregister_private(fb);
 295                drm_framebuffer_cleanup(fb);
 296                kfree(fb);
 297        }
 298        return ret;
 299}
 300
 301void amdgpu_fb_output_poll_changed(struct amdgpu_device *adev)
 302{
 303        if (adev->mode_info.rfbdev)
 304                drm_fb_helper_hotplug_event(&adev->mode_info.rfbdev->helper);
 305}
 306
 307static int amdgpu_fbdev_destroy(struct drm_device *dev, struct amdgpu_fbdev *rfbdev)
 308{
 309        struct amdgpu_framebuffer *rfb = &rfbdev->rfb;
 310
 311        drm_fb_helper_unregister_fbi(&rfbdev->helper);
 312        drm_fb_helper_release_fbi(&rfbdev->helper);
 313
 314        if (rfb->obj) {
 315                amdgpufb_destroy_pinned_object(rfb->obj);
 316                rfb->obj = NULL;
 317        }
 318        drm_fb_helper_fini(&rfbdev->helper);
 319        drm_framebuffer_unregister_private(&rfb->base);
 320        drm_framebuffer_cleanup(&rfb->base);
 321
 322        return 0;
 323}
 324
 325/** Sets the color ramps on behalf of fbcon */
 326static void amdgpu_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
 327                                      u16 blue, int regno)
 328{
 329        struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
 330
 331        amdgpu_crtc->lut_r[regno] = red >> 6;
 332        amdgpu_crtc->lut_g[regno] = green >> 6;
 333        amdgpu_crtc->lut_b[regno] = blue >> 6;
 334}
 335
 336/** Gets the color ramps on behalf of fbcon */
 337static void amdgpu_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 338                                      u16 *blue, int regno)
 339{
 340        struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
 341
 342        *red = amdgpu_crtc->lut_r[regno] << 6;
 343        *green = amdgpu_crtc->lut_g[regno] << 6;
 344        *blue = amdgpu_crtc->lut_b[regno] << 6;
 345}
 346
 347static const struct drm_fb_helper_funcs amdgpu_fb_helper_funcs = {
 348        .gamma_set = amdgpu_crtc_fb_gamma_set,
 349        .gamma_get = amdgpu_crtc_fb_gamma_get,
 350        .fb_probe = amdgpufb_create,
 351};
 352
 353int amdgpu_fbdev_init(struct amdgpu_device *adev)
 354{
 355        struct amdgpu_fbdev *rfbdev;
 356        int bpp_sel = 32;
 357        int ret;
 358
 359        /* don't init fbdev on hw without DCE */
 360        if (!adev->mode_info.mode_config_initialized)
 361                return 0;
 362
 363        /* don't init fbdev if there are no connectors */
 364        if (list_empty(&adev->ddev->mode_config.connector_list))
 365                return 0;
 366
 367        /* select 8 bpp console on low vram cards */
 368        if (adev->mc.real_vram_size <= (32*1024*1024))
 369                bpp_sel = 8;
 370
 371        rfbdev = kzalloc(sizeof(struct amdgpu_fbdev), GFP_KERNEL);
 372        if (!rfbdev)
 373                return -ENOMEM;
 374
 375        rfbdev->adev = adev;
 376        adev->mode_info.rfbdev = rfbdev;
 377
 378        drm_fb_helper_prepare(adev->ddev, &rfbdev->helper,
 379                        &amdgpu_fb_helper_funcs);
 380
 381        ret = drm_fb_helper_init(adev->ddev, &rfbdev->helper,
 382                                 adev->mode_info.num_crtc,
 383                                 AMDGPUFB_CONN_LIMIT);
 384        if (ret) {
 385                kfree(rfbdev);
 386                return ret;
 387        }
 388
 389        drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
 390
 391        /* disable all the possible outputs/crtcs before entering KMS mode */
 392        drm_helper_disable_unused_functions(adev->ddev);
 393
 394        drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
 395        return 0;
 396}
 397
 398void amdgpu_fbdev_fini(struct amdgpu_device *adev)
 399{
 400        if (!adev->mode_info.rfbdev)
 401                return;
 402
 403        amdgpu_fbdev_destroy(adev->ddev, adev->mode_info.rfbdev);
 404        kfree(adev->mode_info.rfbdev);
 405        adev->mode_info.rfbdev = NULL;
 406}
 407
 408void amdgpu_fbdev_set_suspend(struct amdgpu_device *adev, int state)
 409{
 410        if (adev->mode_info.rfbdev)
 411                drm_fb_helper_set_suspend(&adev->mode_info.rfbdev->helper,
 412                        state);
 413}
 414
 415int amdgpu_fbdev_total_size(struct amdgpu_device *adev)
 416{
 417        struct amdgpu_bo *robj;
 418        int size = 0;
 419
 420        if (!adev->mode_info.rfbdev)
 421                return 0;
 422
 423        robj = gem_to_amdgpu_bo(adev->mode_info.rfbdev->rfb.obj);
 424        size += amdgpu_bo_size(robj);
 425        return size;
 426}
 427
 428bool amdgpu_fbdev_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj)
 429{
 430        if (!adev->mode_info.rfbdev)
 431                return false;
 432        if (robj == gem_to_amdgpu_bo(adev->mode_info.rfbdev->rfb.obj))
 433                return true;
 434        return false;
 435}
 436
 437void amdgpu_fbdev_restore_mode(struct amdgpu_device *adev)
 438{
 439        struct amdgpu_fbdev *afbdev = adev->mode_info.rfbdev;
 440        struct drm_fb_helper *fb_helper;
 441        int ret;
 442
 443        if (!afbdev)
 444                return;
 445
 446        fb_helper = &afbdev->helper;
 447
 448        ret = drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
 449        if (ret)
 450                DRM_DEBUG("failed to restore crtc mode\n");
 451}
 452