1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 Russell King 4 * Written from the i915 driver. 5 */ 6 #include <linux/errno.h> 7 #include <linux/kernel.h> 8 #include <linux/module.h> 9 10 #include <drm/drm_fb_helper.h> 11 #include "armada_crtc.h" 12 #include "armada_drm.h" 13 #include "armada_fb.h" 14 #include "armada_gem.h" 15 16 static /*const*/ struct fb_ops armada_fb_ops = { 17 .owner = THIS_MODULE, 18 DRM_FB_HELPER_DEFAULT_OPS, 19 .fb_fillrect = drm_fb_helper_cfb_fillrect, 20 .fb_copyarea = drm_fb_helper_cfb_copyarea, 21 .fb_imageblit = drm_fb_helper_cfb_imageblit, 22 }; 23 24 static int armada_fbdev_create(struct drm_fb_helper *fbh, 25 struct drm_fb_helper_surface_size *sizes) 26 { 27 struct drm_device *dev = fbh->dev; 28 struct drm_mode_fb_cmd2 mode; 29 struct armada_framebuffer *dfb; 30 struct armada_gem_object *obj; 31 struct fb_info *info; 32 int size, ret; 33 void *ptr; 34 35 memset(&mode, 0, sizeof(mode)); 36 mode.width = sizes->surface_width; 37 mode.height = sizes->surface_height; 38 mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp); 39 mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 40 sizes->surface_depth); 41 42 size = mode.pitches[0] * mode.height; 43 obj = armada_gem_alloc_private_object(dev, size); 44 if (!obj) { 45 DRM_ERROR("failed to allocate fb memory\n"); 46 return -ENOMEM; 47 } 48 49 ret = armada_gem_linear_back(dev, obj); 50 if (ret) { 51 drm_gem_object_put_unlocked(&obj->obj); 52 return ret; 53 } 54 55 ptr = armada_gem_map_object(dev, obj); 56 if (!ptr) { 57 drm_gem_object_put_unlocked(&obj->obj); 58 return -ENOMEM; 59 } 60 61 dfb = armada_framebuffer_create(dev, &mode, obj); 62 63 /* 64 * A reference is now held by the framebuffer object if 65 * successful, otherwise this drops the ref for the error path. 66 */ 67 drm_gem_object_put_unlocked(&obj->obj); 68 69 if (IS_ERR(dfb)) 70 return PTR_ERR(dfb); 71 72 info = drm_fb_helper_alloc_fbi(fbh); 73 if (IS_ERR(info)) { 74 ret = PTR_ERR(info); 75 goto err_fballoc; 76 } 77 78 info->fbops = &armada_fb_ops; 79 info->fix.smem_start = obj->phys_addr; 80 info->fix.smem_len = obj->obj.size; 81 info->screen_size = obj->obj.size; 82 info->screen_base = ptr; 83 fbh->fb = &dfb->fb; 84 85 drm_fb_helper_fill_info(info, fbh, sizes); 86 87 DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n", 88 dfb->fb.width, dfb->fb.height, dfb->fb.format->cpp[0] * 8, 89 (unsigned long long)obj->phys_addr); 90 91 return 0; 92 93 err_fballoc: 94 dfb->fb.funcs->destroy(&dfb->fb); 95 return ret; 96 } 97 98 static int armada_fb_probe(struct drm_fb_helper *fbh, 99 struct drm_fb_helper_surface_size *sizes) 100 { 101 int ret = 0; 102 103 if (!fbh->fb) { 104 ret = armada_fbdev_create(fbh, sizes); 105 if (ret == 0) 106 ret = 1; 107 } 108 return ret; 109 } 110 111 static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { 112 .fb_probe = armada_fb_probe, 113 }; 114 115 int armada_fbdev_init(struct drm_device *dev) 116 { 117 struct armada_private *priv = dev->dev_private; 118 struct drm_fb_helper *fbh; 119 int ret; 120 121 fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL); 122 if (!fbh) 123 return -ENOMEM; 124 125 priv->fbdev = fbh; 126 127 drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); 128 129 ret = drm_fb_helper_init(dev, fbh, 1); 130 if (ret) { 131 DRM_ERROR("failed to initialize drm fb helper\n"); 132 goto err_fb_helper; 133 } 134 135 ret = drm_fb_helper_single_add_all_connectors(fbh); 136 if (ret) { 137 DRM_ERROR("failed to add fb connectors\n"); 138 goto err_fb_setup; 139 } 140 141 ret = drm_fb_helper_initial_config(fbh, 32); 142 if (ret) { 143 DRM_ERROR("failed to set initial config\n"); 144 goto err_fb_setup; 145 } 146 147 return 0; 148 err_fb_setup: 149 drm_fb_helper_fini(fbh); 150 err_fb_helper: 151 priv->fbdev = NULL; 152 return ret; 153 } 154 155 void armada_fbdev_fini(struct drm_device *dev) 156 { 157 struct armada_private *priv = dev->dev_private; 158 struct drm_fb_helper *fbh = priv->fbdev; 159 160 if (fbh) { 161 drm_fb_helper_unregister_fbi(fbh); 162 163 drm_fb_helper_fini(fbh); 164 165 if (fbh->fb) 166 fbh->fb->funcs->destroy(fbh->fb); 167 168 priv->fbdev = NULL; 169 } 170 } 171