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    = cfb_fillrect,
  26        .fb_copyarea    = cfb_copyarea,
  27        .fb_imageblit   = 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 = framebuffer_alloc(0, dev->dev);
  84        if (!info) {
  85                ret = -ENOMEM;
  86                goto err_fballoc;
  87        }
  88
  89        ret = fb_alloc_cmap(&info->cmap, 256, 0);
  90        if (ret) {
  91                ret = -ENOMEM;
  92                goto err_fbcmap;
  93        }
  94
  95        strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id));
  96        info->par = fbh;
  97        info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
  98        info->fbops = &armada_fb_ops;
  99        info->fix.smem_start = obj->phys_addr;
 100        info->fix.smem_len = obj->obj.size;
 101        info->screen_size = obj->obj.size;
 102        info->screen_base = ptr;
 103        fbh->fb = &dfb->fb;
 104        fbh->fbdev = info;
 105        drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth);
 106        drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height);
 107
 108        DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n",
 109                dfb->fb.width, dfb->fb.height, dfb->fb.bits_per_pixel,
 110                (unsigned long long)obj->phys_addr);
 111
 112        return 0;
 113
 114 err_fbcmap:
 115        framebuffer_release(info);
 116 err_fballoc:
 117        dfb->fb.funcs->destroy(&dfb->fb);
 118        return ret;
 119}
 120
 121static int armada_fb_probe(struct drm_fb_helper *fbh,
 122        struct drm_fb_helper_surface_size *sizes)
 123{
 124        int ret = 0;
 125
 126        if (!fbh->fb) {
 127                ret = armada_fb_create(fbh, sizes);
 128                if (ret == 0)
 129                        ret = 1;
 130        }
 131        return ret;
 132}
 133
 134static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
 135        .gamma_set      = armada_drm_crtc_gamma_set,
 136        .gamma_get      = armada_drm_crtc_gamma_get,
 137        .fb_probe       = armada_fb_probe,
 138};
 139
 140int armada_fbdev_init(struct drm_device *dev)
 141{
 142        struct armada_private *priv = dev->dev_private;
 143        struct drm_fb_helper *fbh;
 144        int ret;
 145
 146        fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
 147        if (!fbh)
 148                return -ENOMEM;
 149
 150        priv->fbdev = fbh;
 151
 152        drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 153
 154        ret = drm_fb_helper_init(dev, fbh, 1, 1);
 155        if (ret) {
 156                DRM_ERROR("failed to initialize drm fb helper\n");
 157                goto err_fb_helper;
 158        }
 159
 160        ret = drm_fb_helper_single_add_all_connectors(fbh);
 161        if (ret) {
 162                DRM_ERROR("failed to add fb connectors\n");
 163                goto err_fb_setup;
 164        }
 165
 166        ret = drm_fb_helper_initial_config(fbh, 32);
 167        if (ret) {
 168                DRM_ERROR("failed to set initial config\n");
 169                goto err_fb_setup;
 170        }
 171
 172        return 0;
 173 err_fb_setup:
 174        drm_fb_helper_fini(fbh);
 175 err_fb_helper:
 176        priv->fbdev = NULL;
 177        return ret;
 178}
 179
 180void armada_fbdev_lastclose(struct drm_device *dev)
 181{
 182        struct armada_private *priv = dev->dev_private;
 183
 184        if (priv->fbdev)
 185                drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 186}
 187
 188void armada_fbdev_fini(struct drm_device *dev)
 189{
 190        struct armada_private *priv = dev->dev_private;
 191        struct drm_fb_helper *fbh = priv->fbdev;
 192
 193        if (fbh) {
 194                struct fb_info *info = fbh->fbdev;
 195
 196                if (info) {
 197                        unregister_framebuffer(info);
 198                        if (info->cmap.len)
 199                                fb_dealloc_cmap(&info->cmap);
 200                        framebuffer_release(info);
 201                }
 202
 203                drm_fb_helper_fini(fbh);
 204
 205                if (fbh->fb)
 206                        fbh->fb->funcs->destroy(fbh->fb);
 207
 208                priv->fbdev = NULL;
 209        }
 210}
 211