cirrus_fbdev.c revision 4126d5d61f8466be3f76c1bc4e16d46eb2c9641b
1/* 2 * Copyright 2012 Red Hat 3 * 4 * This file is subject to the terms and conditions of the GNU General 5 * Public License version 2. See the file COPYING in the main 6 * directory of this archive for more details. 7 * 8 * Authors: Matthew Garrett 9 * Dave Airlie 10 */ 11#include <linux/module.h> 12#include "drmP.h" 13#include "drm_fb_helper.h" 14 15#include <linux/fb.h> 16 17#include "cirrus_drv.h" 18 19static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, 20 int x, int y, int width, int height) 21{ 22 int i; 23 struct drm_gem_object *obj; 24 struct cirrus_bo *bo; 25 int src_offset, dst_offset; 26 int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8; 27 int ret; 28 bool unmap = false; 29 30 obj = afbdev->gfb.obj; 31 bo = gem_to_cirrus_bo(obj); 32 33 ret = cirrus_bo_reserve(bo, true); 34 if (ret) { 35 DRM_ERROR("failed to reserve fb bo\n"); 36 return; 37 } 38 39 if (!bo->kmap.virtual) { 40 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); 41 if (ret) { 42 DRM_ERROR("failed to kmap fb updates\n"); 43 cirrus_bo_unreserve(bo); 44 return; 45 } 46 unmap = true; 47 } 48 for (i = y; i < y + height; i++) { 49 /* assume equal stride for now */ 50 src_offset = dst_offset = i * afbdev->gfb.base.pitches[0] + (x * bpp); 51 memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp); 52 53 } 54 if (unmap) 55 ttm_bo_kunmap(&bo->kmap); 56 57 cirrus_bo_unreserve(bo); 58} 59 60static void cirrus_fillrect(struct fb_info *info, 61 const struct fb_fillrect *rect) 62{ 63 struct cirrus_fbdev *afbdev = info->par; 64 sys_fillrect(info, rect); 65 cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width, 66 rect->height); 67} 68 69static void cirrus_copyarea(struct fb_info *info, 70 const struct fb_copyarea *area) 71{ 72 struct cirrus_fbdev *afbdev = info->par; 73 sys_copyarea(info, area); 74 cirrus_dirty_update(afbdev, area->dx, area->dy, area->width, 75 area->height); 76} 77 78static void cirrus_imageblit(struct fb_info *info, 79 const struct fb_image *image) 80{ 81 struct cirrus_fbdev *afbdev = info->par; 82 sys_imageblit(info, image); 83 cirrus_dirty_update(afbdev, image->dx, image->dy, image->width, 84 image->height); 85} 86 87 88static struct fb_ops cirrusfb_ops = { 89 .owner = THIS_MODULE, 90 .fb_check_var = drm_fb_helper_check_var, 91 .fb_set_par = drm_fb_helper_set_par, 92 .fb_fillrect = cirrus_fillrect, 93 .fb_copyarea = cirrus_copyarea, 94 .fb_imageblit = cirrus_imageblit, 95 .fb_pan_display = drm_fb_helper_pan_display, 96 .fb_blank = drm_fb_helper_blank, 97 .fb_setcmap = drm_fb_helper_setcmap, 98}; 99 100static int cirrusfb_create_object(struct cirrus_fbdev *afbdev, 101 struct drm_mode_fb_cmd2 *mode_cmd, 102 struct drm_gem_object **gobj_p) 103{ 104 struct drm_device *dev = afbdev->helper.dev; 105 u32 bpp, depth; 106 u32 size; 107 struct drm_gem_object *gobj; 108 109 int ret = 0; 110 drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); 111 112 if (bpp > 24) 113 return -EINVAL; 114 size = mode_cmd->pitches[0] * mode_cmd->height; 115 ret = cirrus_gem_create(dev, size, true, &gobj); 116 if (ret) 117 return ret; 118 119 *gobj_p = gobj; 120 return ret; 121} 122 123static int cirrusfb_create(struct cirrus_fbdev *gfbdev, 124 struct drm_fb_helper_surface_size *sizes) 125{ 126 struct drm_device *dev = gfbdev->helper.dev; 127 struct cirrus_device *cdev = gfbdev->helper.dev->dev_private; 128 struct fb_info *info; 129 struct drm_framebuffer *fb; 130 struct drm_mode_fb_cmd2 mode_cmd; 131 struct device *device = &dev->pdev->dev; 132 void *sysram; 133 struct drm_gem_object *gobj = NULL; 134 struct cirrus_bo *bo = NULL; 135 int size, ret; 136 137 mode_cmd.width = sizes->surface_width; 138 mode_cmd.height = sizes->surface_height; 139 mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); 140 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 141 sizes->surface_depth); 142 size = mode_cmd.pitches[0] * mode_cmd.height; 143 144 ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj); 145 if (ret) { 146 DRM_ERROR("failed to create fbcon backing object %d\n", ret); 147 return ret; 148 } 149 150 bo = gem_to_cirrus_bo(gobj); 151 152 sysram = vmalloc(size); 153 if (!sysram) 154 return -ENOMEM; 155 156 info = framebuffer_alloc(0, device); 157 if (info == NULL) 158 return -ENOMEM; 159 160 info->par = gfbdev; 161 162 ret = cirrus_framebuffer_init(cdev->dev, &gfbdev->gfb, &mode_cmd, gobj); 163 if (ret) 164 return ret; 165 166 gfbdev->sysram = sysram; 167 gfbdev->size = size; 168 169 fb = &gfbdev->gfb.base; 170 if (!fb) { 171 DRM_INFO("fb is NULL\n"); 172 return -EINVAL; 173 } 174 175 /* setup helper */ 176 gfbdev->helper.fb = fb; 177 gfbdev->helper.fbdev = info; 178 179 strcpy(info->fix.id, "cirrusdrmfb"); 180 181 182 info->flags = FBINFO_DEFAULT; 183 info->fbops = &cirrusfb_ops; 184 185 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); 186 drm_fb_helper_fill_var(info, &gfbdev->helper, sizes->fb_width, 187 sizes->fb_height); 188 189 /* setup aperture base/size for vesafb takeover */ 190 info->apertures = alloc_apertures(1); 191 if (!info->apertures) { 192 ret = -ENOMEM; 193 goto out_iounmap; 194 } 195 info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base; 196 info->apertures->ranges[0].size = cdev->mc.vram_size; 197 198 info->screen_base = sysram; 199 info->screen_size = size; 200 201 info->fix.mmio_start = 0; 202 info->fix.mmio_len = 0; 203 204 ret = fb_alloc_cmap(&info->cmap, 256, 0); 205 if (ret) { 206 DRM_ERROR("%s: can't allocate color map\n", info->fix.id); 207 ret = -ENOMEM; 208 goto out_iounmap; 209 } 210 211 DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); 212 DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start); 213 DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len); 214 DRM_INFO("fb depth is %d\n", fb->depth); 215 DRM_INFO(" pitch is %d\n", fb->pitches[0]); 216 217 return 0; 218out_iounmap: 219 return ret; 220} 221 222static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper, 223 struct drm_fb_helper_surface_size 224 *sizes) 225{ 226 struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; 227 int new_fb = 0; 228 int ret; 229 230 if (!helper->fb) { 231 ret = cirrusfb_create(gfbdev, sizes); 232 if (ret) 233 return ret; 234 new_fb = 1; 235 } 236 return new_fb; 237} 238 239static int cirrus_fbdev_destroy(struct drm_device *dev, 240 struct cirrus_fbdev *gfbdev) 241{ 242 struct fb_info *info; 243 struct cirrus_framebuffer *gfb = &gfbdev->gfb; 244 245 if (gfbdev->helper.fbdev) { 246 info = gfbdev->helper.fbdev; 247 248 unregister_framebuffer(info); 249 if (info->cmap.len) 250 fb_dealloc_cmap(&info->cmap); 251 framebuffer_release(info); 252 } 253 254 if (gfb->obj) { 255 drm_gem_object_unreference_unlocked(gfb->obj); 256 gfb->obj = NULL; 257 } 258 259 vfree(gfbdev->sysram); 260 drm_fb_helper_fini(&gfbdev->helper); 261 drm_framebuffer_cleanup(&gfb->base); 262 263 return 0; 264} 265 266static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { 267 .gamma_set = cirrus_crtc_fb_gamma_set, 268 .gamma_get = cirrus_crtc_fb_gamma_get, 269 .fb_probe = cirrus_fb_find_or_create_single, 270}; 271 272int cirrus_fbdev_init(struct cirrus_device *cdev) 273{ 274 struct cirrus_fbdev *gfbdev; 275 int ret; 276 int bpp_sel = 24; 277 278 /*bpp_sel = 8;*/ 279 gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL); 280 if (!gfbdev) 281 return -ENOMEM; 282 283 cdev->mode_info.gfbdev = gfbdev; 284 gfbdev->helper.funcs = &cirrus_fb_helper_funcs; 285 286 ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, 287 cdev->num_crtc, CIRRUSFB_CONN_LIMIT); 288 if (ret) { 289 kfree(gfbdev); 290 return ret; 291 } 292 drm_fb_helper_single_add_all_connectors(&gfbdev->helper); 293 drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); 294 295 return 0; 296} 297 298void cirrus_fbdev_fini(struct cirrus_device *cdev) 299{ 300 if (!cdev->mode_info.gfbdev) 301 return; 302 303 cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev); 304 kfree(cdev->mode_info.gfbdev); 305 cdev->mode_info.gfbdev = NULL; 306} 307