227 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* exynos_drm_fbdev.c3 *4 * Copyright (c) 2011 Samsung Electronics Co., Ltd.5 * Authors:6 * Inki Dae <inki.dae@samsung.com>7 * Joonyoung Shim <jy0922.shim@samsung.com>8 * Seung-Woo Kim <sw0312.kim@samsung.com>9 */10 11#include <linux/fb.h>12 13#include <drm/drm_crtc_helper.h>14#include <drm/drm_drv.h>15#include <drm/drm_fb_helper.h>16#include <drm/drm_framebuffer.h>17#include <drm/drm_gem_framebuffer_helper.h>18#include <drm/drm_prime.h>19#include <drm/exynos_drm.h>20 21#include "exynos_drm_drv.h"22#include "exynos_drm_fb.h"23#include "exynos_drm_fbdev.h"24 25#define MAX_CONNECTOR 426#define PREFERRED_BPP 3227 28static int exynos_drm_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)29{30 struct drm_fb_helper *helper = info->par;31 struct drm_gem_object *obj = drm_gem_fb_get_obj(helper->fb, 0);32 33 return drm_gem_prime_mmap(obj, vma);34}35 36static void exynos_drm_fb_destroy(struct fb_info *info)37{38 struct drm_fb_helper *fb_helper = info->par;39 struct drm_framebuffer *fb = fb_helper->fb;40 41 drm_fb_helper_fini(fb_helper);42 43 drm_framebuffer_remove(fb);44 45 drm_client_release(&fb_helper->client);46 drm_fb_helper_unprepare(fb_helper);47 kfree(fb_helper);48}49 50static const struct fb_ops exynos_drm_fb_ops = {51 .owner = THIS_MODULE,52 __FB_DEFAULT_DMAMEM_OPS_RDWR,53 DRM_FB_HELPER_DEFAULT_OPS,54 __FB_DEFAULT_DMAMEM_OPS_DRAW,55 .fb_mmap = exynos_drm_fb_mmap,56 .fb_destroy = exynos_drm_fb_destroy,57};58 59static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,60 struct drm_fb_helper_surface_size *sizes,61 struct exynos_drm_gem *exynos_gem)62{63 struct fb_info *fbi;64 struct drm_framebuffer *fb = helper->fb;65 unsigned int size = fb->width * fb->height * fb->format->cpp[0];66 unsigned long offset;67 68 fbi = drm_fb_helper_alloc_info(helper);69 if (IS_ERR(fbi)) {70 DRM_DEV_ERROR(to_dma_dev(helper->dev),71 "failed to allocate fb info.\n");72 return PTR_ERR(fbi);73 }74 75 fbi->fbops = &exynos_drm_fb_ops;76 77 drm_fb_helper_fill_info(fbi, helper, sizes);78 79 offset = fbi->var.xoffset * fb->format->cpp[0];80 offset += fbi->var.yoffset * fb->pitches[0];81 82 fbi->flags |= FBINFO_VIRTFB;83 fbi->screen_buffer = exynos_gem->kvaddr + offset;84 fbi->screen_size = size;85 fbi->fix.smem_len = size;86 87 return 0;88}89 90static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,91 struct drm_fb_helper_surface_size *sizes)92{93 struct exynos_drm_gem *exynos_gem;94 struct drm_device *dev = helper->dev;95 struct drm_mode_fb_cmd2 mode_cmd = { 0 };96 unsigned long size;97 int ret;98 99 DRM_DEV_DEBUG_KMS(dev->dev,100 "surface width(%d), height(%d) and bpp(%d\n",101 sizes->surface_width, sizes->surface_height,102 sizes->surface_bpp);103 104 mode_cmd.width = sizes->surface_width;105 mode_cmd.height = sizes->surface_height;106 mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);107 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,108 sizes->surface_depth);109 110 size = mode_cmd.pitches[0] * mode_cmd.height;111 112 exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true);113 if (IS_ERR(exynos_gem))114 return PTR_ERR(exynos_gem);115 116 helper->fb =117 exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);118 if (IS_ERR(helper->fb)) {119 DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n");120 ret = PTR_ERR(helper->fb);121 goto err_destroy_gem;122 }123 124 ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);125 if (ret < 0)126 goto err_destroy_framebuffer;127 128 return 0;129 130err_destroy_framebuffer:131 drm_framebuffer_cleanup(helper->fb);132 helper->fb = NULL;133err_destroy_gem:134 exynos_drm_gem_destroy(exynos_gem);135 return ret;136}137 138static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {139 .fb_probe = exynos_drm_fbdev_create,140};141 142/*143 * struct drm_client144 */145 146static void exynos_drm_fbdev_client_unregister(struct drm_client_dev *client)147{148 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);149 150 if (fb_helper->info) {151 drm_fb_helper_unregister_info(fb_helper);152 } else {153 drm_client_release(&fb_helper->client);154 drm_fb_helper_unprepare(fb_helper);155 kfree(fb_helper);156 }157}158 159static int exynos_drm_fbdev_client_restore(struct drm_client_dev *client)160{161 drm_fb_helper_lastclose(client->dev);162 163 return 0;164}165 166static int exynos_drm_fbdev_client_hotplug(struct drm_client_dev *client)167{168 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);169 struct drm_device *dev = client->dev;170 int ret;171 172 if (dev->fb_helper)173 return drm_fb_helper_hotplug_event(dev->fb_helper);174 175 ret = drm_fb_helper_init(dev, fb_helper);176 if (ret)177 goto err_drm_err;178 179 if (!drm_drv_uses_atomic_modeset(dev))180 drm_helper_disable_unused_functions(dev);181 182 ret = drm_fb_helper_initial_config(fb_helper);183 if (ret)184 goto err_drm_fb_helper_fini;185 186 return 0;187 188err_drm_fb_helper_fini:189 drm_fb_helper_fini(fb_helper);190err_drm_err:191 drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret);192 return ret;193}194 195static const struct drm_client_funcs exynos_drm_fbdev_client_funcs = {196 .owner = THIS_MODULE,197 .unregister = exynos_drm_fbdev_client_unregister,198 .restore = exynos_drm_fbdev_client_restore,199 .hotplug = exynos_drm_fbdev_client_hotplug,200};201 202void exynos_drm_fbdev_setup(struct drm_device *dev)203{204 struct drm_fb_helper *fb_helper;205 int ret;206 207 drm_WARN(dev, !dev->registered, "Device has not been registered.\n");208 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");209 210 fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);211 if (!fb_helper)212 return;213 drm_fb_helper_prepare(dev, fb_helper, PREFERRED_BPP, &exynos_drm_fb_helper_funcs);214 215 ret = drm_client_init(dev, &fb_helper->client, "fbdev", &exynos_drm_fbdev_client_funcs);216 if (ret)217 goto err_drm_client_init;218 219 drm_client_register(&fb_helper->client);220 221 return;222 223err_drm_client_init:224 drm_fb_helper_unprepare(fb_helper);225 kfree(fb_helper);226}227