linux/drivers/gpu/drm/armada/armada_fbdev.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Russell King
   3 *  Written from the i915 driver.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9#include <linux/errno.h>
  10#include <linux/fb.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13
  14#include <drm/drmP.h>
  15#include <drm/drm_fb_helper.h>
  16#include "armada_crtc.h"
  17#include "armada_drm.h"
  18#include "armada_fb.h"
  19#include "armada_gem.h"
  20
  21static /*const*/ struct fb_ops armada_fb_ops = {
  22        .owner          = THIS_MODULE,
  23        .fb_check_var   = drm_fb_helper_check_var,
  24        .fb_set_par     = drm_fb_helper_set_par,
  25        .fb_fillrect    = drm_fb_helper_cfb_fillrect,
  26        .fb_copyarea    = drm_fb_helper_cfb_copyarea,
  27        .fb_imageblit   = drm_fb_helper_cfb_imageblit,
  28        .fb_pan_display = drm_fb_helper_pan_display,
  29        .fb_blank       = drm_fb_helper_blank,
  30        .fb_setcmap     = drm_fb_helper_setcmap,
  31        .fb_debug_enter = drm_fb_helper_debug_enter,
  32        .fb_debug_leave = drm_fb_helper_debug_leave,
  33};
  34
  35static int armada_fb_create(struct drm_fb_helper *fbh,
  36        struct drm_fb_helper_surface_size *sizes)
  37{
  38        struct drm_device *dev = fbh->dev;
  39        struct drm_mode_fb_cmd2 mode;
  40        struct armada_framebuffer *dfb;
  41        struct armada_gem_object *obj;
  42        struct fb_info *info;
  43        int size, ret;
  44        void *ptr;
  45
  46        memset(&mode, 0, sizeof(mode));
  47        mode.width = sizes->surface_width;
  48        mode.height = sizes->surface_height;
  49        mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
  50        mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
  51                                        sizes->surface_depth);
  52
  53        size = mode.pitches[0] * mode.height;
  54        obj = armada_gem_alloc_private_object(dev, size);
  55        if (!obj) {
  56                DRM_ERROR("failed to allocate fb memory\n");
  57                return -ENOMEM;
  58        }
  59
  60        ret = armada_gem_linear_back(dev, obj);
  61        if (ret) {
  62                drm_gem_object_unreference_unlocked(&obj->obj);
  63                return ret;
  64        }
  65
  66        ptr = armada_gem_map_object(dev, obj);
  67        if (!ptr) {
  68                drm_gem_object_unreference_unlocked(&obj->obj);
  69                return -ENOMEM;
  70        }
  71
  72        dfb = armada_framebuffer_create(dev, &mode, obj);
  73
  74        /*
  75         * A reference is now held by the framebuffer object if
  76         * successful, otherwise this drops the ref for the error path.
  77         */
  78        drm_gem_object_unreference_unlocked(&obj->obj);
  79
  80        if (IS_ERR(dfb))
  81                return PTR_ERR(dfb);
  82
  83        info = drm_fb_helper_alloc_fbi(fbh);
  84        if (IS_ERR(info)) {
  85                ret = PTR_ERR(info);
  86                goto err_fballoc;
  87        }
  88
  89        strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id));
  90        info->par = fbh;
  91        info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
  92        info->fbops = &armada_fb_ops;
  93        info->fix.smem_start = obj->phys_addr;
  94        info->fix.smem_len = obj->obj.size;
  95        info->screen_size = obj->obj.size;
  96        info->screen_base = ptr;
  97        fbh->fb = &dfb->fb;
  98
  99        drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth);
 100        drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height);
 101
 102        DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n",
 103                dfb->fb.width, dfb->fb.height, dfb->fb.bits_per_pixel,
 104                (unsigned long long)obj->phys_addr);
 105
 106        return 0;
 107
 108 err_fballoc:
 109        dfb->fb.funcs->destroy(&dfb->fb);
 110        return ret;
 111}
 112
 113static int armada_fb_probe(struct drm_fb_helper *fbh,
 114        struct drm_fb_helper_surface_size *sizes)
 115{
 116        int ret = 0;
 117
 118        if (!fbh->fb) {
 119                ret = armada_fb_create(fbh, sizes);
 120                if (ret == 0)
 121                        ret = 1;
 122        }
 123        return ret;
 124}
 125
 126static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
 127        .gamma_set      = armada_drm_crtc_gamma_set,
 128        .gamma_get      = armada_drm_crtc_gamma_get,
 129        .fb_probe       = armada_fb_probe,
 130};
 131
 132int armada_fbdev_init(struct drm_device *dev)
 133{
 134        struct armada_private *priv = dev->dev_private;
 135        struct drm_fb_helper *fbh;
 136        int ret;
 137
 138        fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
 139        if (!fbh)
 140                return -ENOMEM;
 141
 142        priv->fbdev = fbh;
 143
 144        drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 145
 146        ret = drm_fb_helper_init(dev, fbh, 1, 1);
 147        if (ret) {
 148                DRM_ERROR("failed to initialize drm fb helper\n");
 149                goto err_fb_helper;
 150        }
 151
 152        ret = drm_fb_helper_single_add_all_connectors(fbh);
 153        if (ret) {
 154                DRM_ERROR("failed to add fb connectors\n");
 155                goto err_fb_setup;
 156        }
 157
 158        ret = drm_fb_helper_initial_config(fbh, 32);
 159        if (ret) {
 160                DRM_ERROR("failed to set initial config\n");
 161                goto err_fb_setup;
 162        }
 163
 164        return 0;
 165 err_fb_setup:
 166        drm_fb_helper_release_fbi(fbh);
 167        drm_fb_helper_fini(fbh);
 168 err_fb_helper:
 169        priv->fbdev = NULL;
 170        return ret;
 171}
 172
 173void armada_fbdev_lastclose(struct drm_device *dev)
 174{
 175        struct armada_private *priv = dev->dev_private;
 176
 177        if (priv->fbdev)
 178                drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 179}
 180
 181void armada_fbdev_fini(struct drm_device *dev)
 182{
 183        struct armada_private *priv = dev->dev_private;
 184        struct drm_fb_helper *fbh = priv->fbdev;
 185
 186        if (fbh) {
 187                drm_fb_helper_unregister_fbi(fbh);
 188                drm_fb_helper_release_fbi(fbh);
 189
 190                drm_fb_helper_fini(fbh);
 191
 192                if (fbh->fb)
 193                        fbh->fb->funcs->destroy(fbh->fb);
 194
 195                priv->fbdev = NULL;
 196        }
 197}
 198