linux/drivers/gpu/drm/xylon/xylon_fbdev.c
<<
>>
Prefs
   1/*
   2 * Xylon DRM driver fb device functions
   3 *
   4 * Copyright (C) 2014 Xylon d.o.o.
   5 * Author: Davor Joja <davor.joja@logicbricks.com>
   6 *
   7 * This software is licensed under the terms of the GNU General Public
   8 * License version 2, as published by the Free Software Foundation, and
   9 * may be copied, distributed, and modified under those terms.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <drm/drmP.h>
  18#include <drm/drm_crtc_helper.h>
  19#include <drm/drm_fb_helper.h>
  20#include <drm/drm_gem_cma_helper.h>
  21
  22#include "xylon_crtc.h"
  23#include "xylon_drv.h"
  24#include "xylon_fb.h"
  25#include "xylon_fbdev.h"
  26
  27struct xylon_drm_fb_device {
  28        struct drm_fb_helper fb_helper;
  29};
  30
  31static struct fb_ops xylon_drm_fbdev_ops = {
  32        .owner = THIS_MODULE,
  33        .fb_fillrect = sys_fillrect,
  34        .fb_copyarea = sys_copyarea,
  35        .fb_imageblit = sys_imageblit,
  36        .fb_check_var = drm_fb_helper_check_var,
  37        .fb_set_par = drm_fb_helper_set_par,
  38        .fb_blank = drm_fb_helper_blank,
  39        .fb_pan_display = drm_fb_helper_pan_display,
  40        .fb_setcmap = drm_fb_helper_setcmap,
  41};
  42
  43static int xylon_drm_fbdev_create(struct drm_fb_helper *helper,
  44                                  struct drm_fb_helper_surface_size *sizes)
  45{
  46        struct drm_device *dev = helper->dev;
  47        struct drm_framebuffer *fb;
  48        struct drm_gem_cma_object *obj;
  49        struct drm_mode_fb_cmd2 mode_cmd;
  50        struct fb_info *fbi;
  51        struct xylon_drm_device *xdev = dev->dev_private;
  52        unsigned long offset;
  53        unsigned int bytes_per_pixel;
  54        unsigned int buff_width;
  55        size_t size;
  56        int ret;
  57
  58        ret = xylon_drm_crtc_get_param(xdev->crtc, &buff_width,
  59                                       XYLON_DRM_CRTC_BUFF_WIDTH);
  60        if (ret)
  61                return ret;
  62
  63        bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
  64
  65        memset(&mode_cmd, 0, sizeof(mode_cmd));
  66
  67        mode_cmd.width = sizes->surface_width;
  68        mode_cmd.height = sizes->surface_height;
  69        mode_cmd.pitches[0] = buff_width * bytes_per_pixel;
  70        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
  71                                                          sizes->surface_depth);
  72
  73        size = mode_cmd.pitches[0] * mode_cmd.height;
  74
  75        obj = drm_gem_cma_create(dev, size);
  76        if (IS_ERR(obj))
  77                return -ENOMEM;
  78
  79        fb = xylon_drm_fb_init(dev, &mode_cmd, &obj->base);
  80        if (IS_ERR(fb)) {
  81                DRM_ERROR("failed initialize fb\n");
  82                goto err_fb_init;
  83        }
  84
  85        fbi = framebuffer_alloc(0, dev->dev);
  86        if (!fbi) {
  87                DRM_ERROR("failed allocate framebuffer info\n");
  88                ret = -ENOMEM;
  89                goto err_fb_alloc;
  90        }
  91
  92        helper->fb = fb;
  93        helper->fbdev = fbi;
  94
  95        fbi->par = helper;
  96        fbi->flags = FBINFO_FLAG_DEFAULT;
  97        fbi->fbops = &xylon_drm_fbdev_ops;
  98
  99        ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
 100        if (ret) {
 101                DRM_ERROR("failed allocate color map\n");
 102                goto err_fb_alloc_cmap;
 103        }
 104
 105        drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
 106        drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
 107
 108        offset = fbi->var.xoffset * bytes_per_pixel;
 109        offset += fbi->var.yoffset * fb->pitches[0];
 110
 111        dev->mode_config.fb_base = (resource_size_t)obj->paddr;
 112        fbi->screen_base = (char __iomem *)(obj->vaddr + offset);
 113        fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
 114        fbi->screen_size = size;
 115        fbi->fix.smem_len = size;
 116
 117        return 0;
 118
 119err_fb_alloc_cmap:
 120        drm_framebuffer_unregister_private(fb);
 121        drm_framebuffer_remove(fb);
 122err_fb_init:
 123        framebuffer_release(fbi);
 124err_fb_alloc:
 125        drm_gem_cma_free_object(&obj->base);
 126
 127        return ret;
 128}
 129
 130static const struct drm_fb_helper_funcs xylon_drm_fbdev_helper_funcs = {
 131        .fb_probe = xylon_drm_fbdev_create,
 132};
 133
 134struct xylon_drm_fb_device *
 135xylon_drm_fbdev_init(struct drm_device *dev,
 136                     unsigned int preferred_bpp, unsigned int num_crtc,
 137                     unsigned int max_conn_count)
 138{
 139        struct drm_fb_helper *helper;
 140        struct xylon_drm_fb_device *fbdev;
 141        int ret;
 142
 143        fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 144        if (!fbdev) {
 145                DRM_ERROR("failed allocate fbdev\n");
 146                return ERR_PTR(-ENOMEM);
 147        }
 148
 149        helper = &fbdev->fb_helper;
 150
 151        drm_fb_helper_prepare(dev, helper, &xylon_drm_fbdev_helper_funcs);
 152
 153        ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
 154        if (ret < 0) {
 155                DRM_ERROR("failed fb init\n");
 156                goto err_fb_helper_init;
 157        }
 158
 159        ret = drm_fb_helper_single_add_all_connectors(helper);
 160        if (ret < 0) {
 161                DRM_ERROR("failed add connectors\n");
 162                goto err_fb_helper_single_add;
 163        }
 164
 165        drm_helper_disable_unused_functions(dev);
 166
 167        if (drm_fb_helper_initial_config(helper, preferred_bpp)) {
 168                DRM_ERROR("failed fb initial config\n");
 169                ret = -EINVAL;
 170                goto err_fb_helper_single_add;
 171        }
 172
 173        return fbdev;
 174
 175err_fb_helper_single_add:
 176        drm_fb_helper_fini(helper);
 177err_fb_helper_init:
 178        kfree(fbdev);
 179
 180        return ERR_PTR(ret);
 181}
 182
 183void xylon_drm_fbdev_fini(struct xylon_drm_fb_device *fbdev)
 184{
 185        struct fb_info *info;
 186        int ret;
 187
 188        if (fbdev->fb_helper.fbdev) {
 189                info = fbdev->fb_helper.fbdev;
 190
 191                ret = unregister_framebuffer(info);
 192                if (ret < 0)
 193                        DRM_INFO("failed unregister fb\n");
 194
 195                if (info->cmap.len)
 196                        fb_dealloc_cmap(&info->cmap);
 197
 198                framebuffer_release(info);
 199        }
 200
 201        drm_framebuffer_unregister_private(fbdev->fb_helper.fb);
 202        drm_framebuffer_remove(fbdev->fb_helper.fb);
 203
 204        drm_fb_helper_fini(&fbdev->fb_helper);
 205
 206        kfree(fbdev);
 207}
 208
 209void xylon_drm_fbdev_restore_mode(struct xylon_drm_fb_device *fbdev)
 210{
 211        struct drm_device *dev;
 212
 213        if (fbdev) {
 214                dev = fbdev->fb_helper.dev;
 215
 216                drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->fb_helper);
 217        }
 218}
 219
 220void xylon_drm_fbdev_hotplug_event(struct xylon_drm_fb_device *fbdev)
 221{
 222        if (fbdev)
 223                drm_fb_helper_hotplug_event(&fbdev->fb_helper);
 224}
 225