Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* exynos_drm_fbdev.c
0003  *
0004  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
0005  * Authors:
0006  *  Inki Dae <inki.dae@samsung.com>
0007  *  Joonyoung Shim <jy0922.shim@samsung.com>
0008  *  Seung-Woo Kim <sw0312.kim@samsung.com>
0009  */
0010 
0011 #include <linux/console.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/vmalloc.h>
0014 
0015 #include <drm/drm_crtc.h>
0016 #include <drm/drm_fb_helper.h>
0017 #include <drm/drm_fourcc.h>
0018 #include <drm/drm_framebuffer.h>
0019 #include <drm/drm_prime.h>
0020 #include <drm/drm_probe_helper.h>
0021 #include <drm/exynos_drm.h>
0022 
0023 #include "exynos_drm_drv.h"
0024 #include "exynos_drm_fb.h"
0025 #include "exynos_drm_fbdev.h"
0026 
0027 #define MAX_CONNECTOR       4
0028 #define PREFERRED_BPP       32
0029 
0030 #define to_exynos_fbdev(x)  container_of(x, struct exynos_drm_fbdev,\
0031                 drm_fb_helper)
0032 
0033 struct exynos_drm_fbdev {
0034     struct drm_fb_helper    drm_fb_helper;
0035     struct exynos_drm_gem   *exynos_gem;
0036 };
0037 
0038 static int exynos_drm_fb_mmap(struct fb_info *info,
0039             struct vm_area_struct *vma)
0040 {
0041     struct drm_fb_helper *helper = info->par;
0042     struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper);
0043     struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
0044 
0045     return drm_gem_prime_mmap(&exynos_gem->base, vma);
0046 }
0047 
0048 static const struct fb_ops exynos_drm_fb_ops = {
0049     .owner      = THIS_MODULE,
0050     DRM_FB_HELPER_DEFAULT_OPS,
0051     .fb_mmap        = exynos_drm_fb_mmap,
0052     .fb_fillrect    = drm_fb_helper_cfb_fillrect,
0053     .fb_copyarea    = drm_fb_helper_cfb_copyarea,
0054     .fb_imageblit   = drm_fb_helper_cfb_imageblit,
0055 };
0056 
0057 static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
0058                    struct drm_fb_helper_surface_size *sizes,
0059                    struct exynos_drm_gem *exynos_gem)
0060 {
0061     struct fb_info *fbi;
0062     struct drm_framebuffer *fb = helper->fb;
0063     unsigned int size = fb->width * fb->height * fb->format->cpp[0];
0064     unsigned long offset;
0065 
0066     fbi = drm_fb_helper_alloc_fbi(helper);
0067     if (IS_ERR(fbi)) {
0068         DRM_DEV_ERROR(to_dma_dev(helper->dev),
0069                   "failed to allocate fb info.\n");
0070         return PTR_ERR(fbi);
0071     }
0072 
0073     fbi->fbops = &exynos_drm_fb_ops;
0074 
0075     drm_fb_helper_fill_info(fbi, helper, sizes);
0076 
0077     offset = fbi->var.xoffset * fb->format->cpp[0];
0078     offset += fbi->var.yoffset * fb->pitches[0];
0079 
0080     fbi->screen_buffer = exynos_gem->kvaddr + offset;
0081     fbi->screen_size = size;
0082     fbi->fix.smem_len = size;
0083 
0084     return 0;
0085 }
0086 
0087 static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
0088                     struct drm_fb_helper_surface_size *sizes)
0089 {
0090     struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
0091     struct exynos_drm_gem *exynos_gem;
0092     struct drm_device *dev = helper->dev;
0093     struct drm_mode_fb_cmd2 mode_cmd = { 0 };
0094     unsigned long size;
0095     int ret;
0096 
0097     DRM_DEV_DEBUG_KMS(dev->dev,
0098               "surface width(%d), height(%d) and bpp(%d\n",
0099               sizes->surface_width, sizes->surface_height,
0100               sizes->surface_bpp);
0101 
0102     mode_cmd.width = sizes->surface_width;
0103     mode_cmd.height = sizes->surface_height;
0104     mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
0105     mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
0106                               sizes->surface_depth);
0107 
0108     size = mode_cmd.pitches[0] * mode_cmd.height;
0109 
0110     exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true);
0111     if (IS_ERR(exynos_gem))
0112         return PTR_ERR(exynos_gem);
0113 
0114     exynos_fbdev->exynos_gem = exynos_gem;
0115 
0116     helper->fb =
0117         exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
0118     if (IS_ERR(helper->fb)) {
0119         DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n");
0120         ret = PTR_ERR(helper->fb);
0121         goto err_destroy_gem;
0122     }
0123 
0124     ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
0125     if (ret < 0)
0126         goto err_destroy_framebuffer;
0127 
0128     return ret;
0129 
0130 err_destroy_framebuffer:
0131     drm_framebuffer_cleanup(helper->fb);
0132 err_destroy_gem:
0133     exynos_drm_gem_destroy(exynos_gem);
0134 
0135     /*
0136      * if failed, all resources allocated above would be released by
0137      * drm_mode_config_cleanup() when drm_load() had been called prior
0138      * to any specific driver such as fimd or hdmi driver.
0139      */
0140 
0141     return ret;
0142 }
0143 
0144 static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
0145     .fb_probe = exynos_drm_fbdev_create,
0146 };
0147 
0148 int exynos_drm_fbdev_init(struct drm_device *dev)
0149 {
0150     struct exynos_drm_fbdev *fbdev;
0151     struct exynos_drm_private *private = dev->dev_private;
0152     struct drm_fb_helper *helper;
0153     int ret;
0154 
0155     if (!dev->mode_config.num_crtc)
0156         return 0;
0157 
0158     fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
0159     if (!fbdev)
0160         return -ENOMEM;
0161 
0162     private->fb_helper = helper = &fbdev->drm_fb_helper;
0163 
0164     drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
0165 
0166     ret = drm_fb_helper_init(dev, helper);
0167     if (ret < 0) {
0168         DRM_DEV_ERROR(dev->dev,
0169                   "failed to initialize drm fb helper.\n");
0170         goto err_init;
0171     }
0172 
0173     ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
0174     if (ret < 0) {
0175         DRM_DEV_ERROR(dev->dev,
0176                   "failed to set up hw configuration.\n");
0177         goto err_setup;
0178     }
0179 
0180     return 0;
0181 
0182 err_setup:
0183     drm_fb_helper_fini(helper);
0184 
0185 err_init:
0186     private->fb_helper = NULL;
0187     kfree(fbdev);
0188 
0189     return ret;
0190 }
0191 
0192 static void exynos_drm_fbdev_destroy(struct drm_device *dev,
0193                       struct drm_fb_helper *fb_helper)
0194 {
0195     struct drm_framebuffer *fb;
0196 
0197     /* release drm framebuffer and real buffer */
0198     if (fb_helper->fb && fb_helper->fb->funcs) {
0199         fb = fb_helper->fb;
0200         if (fb)
0201             drm_framebuffer_remove(fb);
0202     }
0203 
0204     drm_fb_helper_unregister_fbi(fb_helper);
0205 
0206     drm_fb_helper_fini(fb_helper);
0207 }
0208 
0209 void exynos_drm_fbdev_fini(struct drm_device *dev)
0210 {
0211     struct exynos_drm_private *private = dev->dev_private;
0212     struct exynos_drm_fbdev *fbdev;
0213 
0214     if (!private || !private->fb_helper)
0215         return;
0216 
0217     fbdev = to_exynos_fbdev(private->fb_helper);
0218 
0219     exynos_drm_fbdev_destroy(dev, private->fb_helper);
0220     kfree(fbdev);
0221     private->fb_helper = NULL;
0222 }
0223