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