linux/drivers/gpu/drm/armada/armada_fbdev.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 Russell King
   4 *  Written from the i915 driver.
   5 */
   6#include <linux/errno.h>
   7#include <linux/kernel.h>
   8#include <linux/module.h>
   9
  10#include <drm/drm_fb_helper.h>
  11#include "armada_crtc.h"
  12#include "armada_drm.h"
  13#include "armada_fb.h"
  14#include "armada_gem.h"
  15
  16static /*const*/ struct fb_ops armada_fb_ops = {
  17        .owner          = THIS_MODULE,
  18        DRM_FB_HELPER_DEFAULT_OPS,
  19        .fb_fillrect    = drm_fb_helper_cfb_fillrect,
  20        .fb_copyarea    = drm_fb_helper_cfb_copyarea,
  21        .fb_imageblit   = drm_fb_helper_cfb_imageblit,
  22};
  23
  24static int armada_fbdev_create(struct drm_fb_helper *fbh,
  25        struct drm_fb_helper_surface_size *sizes)
  26{
  27        struct drm_device *dev = fbh->dev;
  28        struct drm_mode_fb_cmd2 mode;
  29        struct armada_framebuffer *dfb;
  30        struct armada_gem_object *obj;
  31        struct fb_info *info;
  32        int size, ret;
  33        void *ptr;
  34
  35        memset(&mode, 0, sizeof(mode));
  36        mode.width = sizes->surface_width;
  37        mode.height = sizes->surface_height;
  38        mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
  39        mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
  40                                        sizes->surface_depth);
  41
  42        size = mode.pitches[0] * mode.height;
  43        obj = armada_gem_alloc_private_object(dev, size);
  44        if (!obj) {
  45                DRM_ERROR("failed to allocate fb memory\n");
  46                return -ENOMEM;
  47        }
  48
  49        ret = armada_gem_linear_back(dev, obj);
  50        if (ret) {
  51                drm_gem_object_put_unlocked(&obj->obj);
  52                return ret;
  53        }
  54
  55        ptr = armada_gem_map_object(dev, obj);
  56        if (!ptr) {
  57                drm_gem_object_put_unlocked(&obj->obj);
  58                return -ENOMEM;
  59        }
  60
  61        dfb = armada_framebuffer_create(dev, &mode, obj);
  62
  63        /*
  64         * A reference is now held by the framebuffer object if
  65         * successful, otherwise this drops the ref for the error path.
  66         */
  67        drm_gem_object_put_unlocked(&obj->obj);
  68
  69        if (IS_ERR(dfb))
  70                return PTR_ERR(dfb);
  71
  72        info = drm_fb_helper_alloc_fbi(fbh);
  73        if (IS_ERR(info)) {
  74                ret = PTR_ERR(info);
  75                goto err_fballoc;
  76        }
  77
  78        info->fbops = &armada_fb_ops;
  79        info->fix.smem_start = obj->phys_addr;
  80        info->fix.smem_len = obj->obj.size;
  81        info->screen_size = obj->obj.size;
  82        info->screen_base = ptr;
  83        fbh->fb = &dfb->fb;
  84
  85        drm_fb_helper_fill_info(info, fbh, sizes);
  86
  87        DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n",
  88                dfb->fb.width, dfb->fb.height, dfb->fb.format->cpp[0] * 8,
  89                (unsigned long long)obj->phys_addr);
  90
  91        return 0;
  92
  93 err_fballoc:
  94        dfb->fb.funcs->destroy(&dfb->fb);
  95        return ret;
  96}
  97
  98static int armada_fb_probe(struct drm_fb_helper *fbh,
  99        struct drm_fb_helper_surface_size *sizes)
 100{
 101        int ret = 0;
 102
 103        if (!fbh->fb) {
 104                ret = armada_fbdev_create(fbh, sizes);
 105                if (ret == 0)
 106                        ret = 1;
 107        }
 108        return ret;
 109}
 110
 111static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
 112        .fb_probe       = armada_fb_probe,
 113};
 114
 115int armada_fbdev_init(struct drm_device *dev)
 116{
 117        struct armada_private *priv = dev->dev_private;
 118        struct drm_fb_helper *fbh;
 119        int ret;
 120
 121        fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
 122        if (!fbh)
 123                return -ENOMEM;
 124
 125        priv->fbdev = fbh;
 126
 127        drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 128
 129        ret = drm_fb_helper_init(dev, fbh, 1);
 130        if (ret) {
 131                DRM_ERROR("failed to initialize drm fb helper\n");
 132                goto err_fb_helper;
 133        }
 134
 135        ret = drm_fb_helper_single_add_all_connectors(fbh);
 136        if (ret) {
 137                DRM_ERROR("failed to add fb connectors\n");
 138                goto err_fb_setup;
 139        }
 140
 141        ret = drm_fb_helper_initial_config(fbh, 32);
 142        if (ret) {
 143                DRM_ERROR("failed to set initial config\n");
 144                goto err_fb_setup;
 145        }
 146
 147        return 0;
 148 err_fb_setup:
 149        drm_fb_helper_fini(fbh);
 150 err_fb_helper:
 151        priv->fbdev = NULL;
 152        return ret;
 153}
 154
 155void armada_fbdev_fini(struct drm_device *dev)
 156{
 157        struct armada_private *priv = dev->dev_private;
 158        struct drm_fb_helper *fbh = priv->fbdev;
 159
 160        if (fbh) {
 161                drm_fb_helper_unregister_fbi(fbh);
 162
 163                drm_fb_helper_fini(fbh);
 164
 165                if (fbh->fb)
 166                        fbh->fb->funcs->destroy(fbh->fb);
 167
 168                priv->fbdev = NULL;
 169        }
 170}
 171