257 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013 Red Hat4 * Author: Rob Clark <robdclark@gmail.com>5 */6 7#include <linux/fb.h>8 9#include <drm/drm_drv.h>10#include <drm/drm_crtc_helper.h>11#include <drm/drm_fb_helper.h>12#include <drm/drm_fourcc.h>13#include <drm/drm_framebuffer.h>14#include <drm/drm_prime.h>15 16#include "msm_drv.h"17#include "msm_gem.h"18#include "msm_kms.h"19 20static bool fbdev = true;21MODULE_PARM_DESC(fbdev, "Enable fbdev compat layer");22module_param(fbdev, bool, 0600);23 24/*25 * fbdev funcs, to implement legacy fbdev interface on top of drm driver26 */27 28FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(msm_fbdev,29 drm_fb_helper_damage_range,30 drm_fb_helper_damage_area)31 32static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)33{34 struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par;35 struct drm_gem_object *bo = msm_framebuffer_bo(helper->fb, 0);36 37 return drm_gem_prime_mmap(bo, vma);38}39 40static void msm_fbdev_fb_destroy(struct fb_info *info)41{42 struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par;43 struct drm_framebuffer *fb = helper->fb;44 struct drm_gem_object *bo = msm_framebuffer_bo(fb, 0);45 46 DBG();47 48 drm_fb_helper_fini(helper);49 50 /* this will free the backing object */51 msm_gem_put_vaddr(bo);52 drm_framebuffer_remove(fb);53 54 drm_client_release(&helper->client);55 drm_fb_helper_unprepare(helper);56 kfree(helper);57}58 59static const struct fb_ops msm_fb_ops = {60 .owner = THIS_MODULE,61 __FB_DEFAULT_DEFERRED_OPS_RDWR(msm_fbdev),62 DRM_FB_HELPER_DEFAULT_OPS,63 __FB_DEFAULT_DEFERRED_OPS_DRAW(msm_fbdev),64 .fb_mmap = msm_fbdev_mmap,65 .fb_destroy = msm_fbdev_fb_destroy,66};67 68static int msm_fbdev_create(struct drm_fb_helper *helper,69 struct drm_fb_helper_surface_size *sizes)70{71 struct drm_device *dev = helper->dev;72 struct msm_drm_private *priv = dev->dev_private;73 struct drm_framebuffer *fb = NULL;74 struct drm_gem_object *bo;75 struct fb_info *fbi = NULL;76 uint64_t paddr;77 uint32_t format;78 int ret, pitch;79 80 format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);81 82 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,83 sizes->surface_height, sizes->surface_bpp,84 sizes->fb_width, sizes->fb_height);85 86 pitch = align_pitch(sizes->surface_width, sizes->surface_bpp);87 fb = msm_alloc_stolen_fb(dev, sizes->surface_width,88 sizes->surface_height, pitch, format);89 90 if (IS_ERR(fb)) {91 DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");92 return PTR_ERR(fb);93 }94 95 bo = msm_framebuffer_bo(fb, 0);96 97 /*98 * NOTE: if we can be guaranteed to be able to map buffer99 * in panic (ie. lock-safe, etc) we could avoid pinning the100 * buffer now:101 */102 ret = msm_gem_get_and_pin_iova(bo, priv->kms->aspace, &paddr);103 if (ret) {104 DRM_DEV_ERROR(dev->dev, "failed to get buffer obj iova: %d\n", ret);105 goto fail;106 }107 108 fbi = drm_fb_helper_alloc_info(helper);109 if (IS_ERR(fbi)) {110 DRM_DEV_ERROR(dev->dev, "failed to allocate fb info\n");111 ret = PTR_ERR(fbi);112 goto fail;113 }114 115 DBG("fbi=%p, dev=%p", fbi, dev);116 117 helper->fb = fb;118 119 fbi->fbops = &msm_fb_ops;120 121 drm_fb_helper_fill_info(fbi, helper, sizes);122 123 fbi->screen_buffer = msm_gem_get_vaddr(bo);124 if (IS_ERR(fbi->screen_buffer)) {125 ret = PTR_ERR(fbi->screen_buffer);126 goto fail;127 }128 fbi->screen_size = bo->size;129 fbi->fix.smem_start = paddr;130 fbi->fix.smem_len = bo->size;131 132 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);133 DBG("allocated %dx%d fb", fb->width, fb->height);134 135 return 0;136 137fail:138 drm_framebuffer_remove(fb);139 return ret;140}141 142static int msm_fbdev_fb_dirty(struct drm_fb_helper *helper,143 struct drm_clip_rect *clip)144{145 struct drm_device *dev = helper->dev;146 int ret;147 148 /* Call damage handlers only if necessary */149 if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))150 return 0;151 152 if (helper->fb->funcs->dirty) {153 ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);154 if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))155 return ret;156 }157 158 return 0;159}160 161static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {162 .fb_probe = msm_fbdev_create,163 .fb_dirty = msm_fbdev_fb_dirty,164};165 166/*167 * struct drm_client168 */169 170static void msm_fbdev_client_unregister(struct drm_client_dev *client)171{172 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);173 174 if (fb_helper->info) {175 drm_fb_helper_unregister_info(fb_helper);176 } else {177 drm_client_release(&fb_helper->client);178 drm_fb_helper_unprepare(fb_helper);179 kfree(fb_helper);180 }181}182 183static int msm_fbdev_client_restore(struct drm_client_dev *client)184{185 drm_fb_helper_lastclose(client->dev);186 187 return 0;188}189 190static int msm_fbdev_client_hotplug(struct drm_client_dev *client)191{192 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);193 struct drm_device *dev = client->dev;194 int ret;195 196 if (dev->fb_helper)197 return drm_fb_helper_hotplug_event(dev->fb_helper);198 199 ret = drm_fb_helper_init(dev, fb_helper);200 if (ret)201 goto err_drm_err;202 203 if (!drm_drv_uses_atomic_modeset(dev))204 drm_helper_disable_unused_functions(dev);205 206 ret = drm_fb_helper_initial_config(fb_helper);207 if (ret)208 goto err_drm_fb_helper_fini;209 210 return 0;211 212err_drm_fb_helper_fini:213 drm_fb_helper_fini(fb_helper);214err_drm_err:215 drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret);216 return ret;217}218 219static const struct drm_client_funcs msm_fbdev_client_funcs = {220 .owner = THIS_MODULE,221 .unregister = msm_fbdev_client_unregister,222 .restore = msm_fbdev_client_restore,223 .hotplug = msm_fbdev_client_hotplug,224};225 226/* initialize fbdev helper */227void msm_fbdev_setup(struct drm_device *dev)228{229 struct drm_fb_helper *helper;230 int ret;231 232 if (!fbdev)233 return;234 235 drm_WARN(dev, !dev->registered, "Device has not been registered.\n");236 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");237 238 helper = kzalloc(sizeof(*helper), GFP_KERNEL);239 if (!helper)240 return;241 drm_fb_helper_prepare(dev, helper, 32, &msm_fb_helper_funcs);242 243 ret = drm_client_init(dev, &helper->client, "fbdev", &msm_fbdev_client_funcs);244 if (ret) {245 drm_err(dev, "Failed to register client: %d\n", ret);246 goto err_drm_fb_helper_unprepare;247 }248 249 drm_client_register(&helper->client);250 251 return;252 253err_drm_fb_helper_unprepare:254 drm_fb_helper_unprepare(helper);255 kfree(helper);256}257