Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.13-rc3 199 lines 4.7 kB view raw
1/* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2 of the License, or 5 * (at your option) any later version. 6 */ 7 8#include "bochs.h" 9 10/* ---------------------------------------------------------------------- */ 11 12static int bochsfb_mmap(struct fb_info *info, 13 struct vm_area_struct *vma) 14{ 15 struct drm_fb_helper *fb_helper = info->par; 16 struct bochs_device *bochs = 17 container_of(fb_helper, struct bochs_device, fb.helper); 18 struct bochs_bo *bo = gem_to_bochs_bo(bochs->fb.gfb.obj); 19 20 return ttm_fbdev_mmap(vma, &bo->bo); 21} 22 23static struct fb_ops bochsfb_ops = { 24 .owner = THIS_MODULE, 25 DRM_FB_HELPER_DEFAULT_OPS, 26 .fb_fillrect = drm_fb_helper_sys_fillrect, 27 .fb_copyarea = drm_fb_helper_sys_copyarea, 28 .fb_imageblit = drm_fb_helper_sys_imageblit, 29 .fb_mmap = bochsfb_mmap, 30}; 31 32static int bochsfb_create_object(struct bochs_device *bochs, 33 const struct drm_mode_fb_cmd2 *mode_cmd, 34 struct drm_gem_object **gobj_p) 35{ 36 struct drm_device *dev = bochs->dev; 37 struct drm_gem_object *gobj; 38 u32 size; 39 int ret = 0; 40 41 size = mode_cmd->pitches[0] * mode_cmd->height; 42 ret = bochs_gem_create(dev, size, true, &gobj); 43 if (ret) 44 return ret; 45 46 *gobj_p = gobj; 47 return ret; 48} 49 50static int bochsfb_create(struct drm_fb_helper *helper, 51 struct drm_fb_helper_surface_size *sizes) 52{ 53 struct bochs_device *bochs = 54 container_of(helper, struct bochs_device, fb.helper); 55 struct fb_info *info; 56 struct drm_framebuffer *fb; 57 struct drm_mode_fb_cmd2 mode_cmd; 58 struct drm_gem_object *gobj = NULL; 59 struct bochs_bo *bo = NULL; 60 int size, ret; 61 62 if (sizes->surface_bpp != 32) 63 return -EINVAL; 64 65 mode_cmd.width = sizes->surface_width; 66 mode_cmd.height = sizes->surface_height; 67 mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); 68 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 69 sizes->surface_depth); 70 size = mode_cmd.pitches[0] * mode_cmd.height; 71 72 /* alloc, pin & map bo */ 73 ret = bochsfb_create_object(bochs, &mode_cmd, &gobj); 74 if (ret) { 75 DRM_ERROR("failed to create fbcon backing object %d\n", ret); 76 return ret; 77 } 78 79 bo = gem_to_bochs_bo(gobj); 80 81 ret = ttm_bo_reserve(&bo->bo, true, false, NULL); 82 if (ret) 83 return ret; 84 85 ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL); 86 if (ret) { 87 DRM_ERROR("failed to pin fbcon\n"); 88 ttm_bo_unreserve(&bo->bo); 89 return ret; 90 } 91 92 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, 93 &bo->kmap); 94 if (ret) { 95 DRM_ERROR("failed to kmap fbcon\n"); 96 ttm_bo_unreserve(&bo->bo); 97 return ret; 98 } 99 100 ttm_bo_unreserve(&bo->bo); 101 102 /* init fb device */ 103 info = drm_fb_helper_alloc_fbi(helper); 104 if (IS_ERR(info)) 105 return PTR_ERR(info); 106 107 info->par = &bochs->fb.helper; 108 109 ret = bochs_framebuffer_init(bochs->dev, &bochs->fb.gfb, &mode_cmd, gobj); 110 if (ret) 111 return ret; 112 113 bochs->fb.size = size; 114 115 /* setup helper */ 116 fb = &bochs->fb.gfb.base; 117 bochs->fb.helper.fb = fb; 118 119 strcpy(info->fix.id, "bochsdrmfb"); 120 121 info->flags = FBINFO_DEFAULT; 122 info->fbops = &bochsfb_ops; 123 124 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); 125 drm_fb_helper_fill_var(info, &bochs->fb.helper, sizes->fb_width, 126 sizes->fb_height); 127 128 info->screen_base = bo->kmap.virtual; 129 info->screen_size = size; 130 131 drm_vma_offset_remove(&bo->bo.bdev->vma_manager, &bo->bo.vma_node); 132 info->fix.smem_start = 0; 133 info->fix.smem_len = size; 134 135 bochs->fb.initialized = true; 136 return 0; 137} 138 139static int bochs_fbdev_destroy(struct bochs_device *bochs) 140{ 141 struct bochs_framebuffer *gfb = &bochs->fb.gfb; 142 143 DRM_DEBUG_DRIVER("\n"); 144 145 drm_fb_helper_unregister_fbi(&bochs->fb.helper); 146 147 if (gfb->obj) { 148 drm_gem_object_unreference_unlocked(gfb->obj); 149 gfb->obj = NULL; 150 } 151 152 drm_framebuffer_unregister_private(&gfb->base); 153 drm_framebuffer_cleanup(&gfb->base); 154 155 return 0; 156} 157 158static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = { 159 .fb_probe = bochsfb_create, 160}; 161 162int bochs_fbdev_init(struct bochs_device *bochs) 163{ 164 int ret; 165 166 drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, 167 &bochs_fb_helper_funcs); 168 169 ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 1); 170 if (ret) 171 return ret; 172 173 ret = drm_fb_helper_single_add_all_connectors(&bochs->fb.helper); 174 if (ret) 175 goto fini; 176 177 drm_helper_disable_unused_functions(bochs->dev); 178 179 ret = drm_fb_helper_initial_config(&bochs->fb.helper, 32); 180 if (ret) 181 goto fini; 182 183 return 0; 184 185fini: 186 drm_fb_helper_fini(&bochs->fb.helper); 187 return ret; 188} 189 190void bochs_fbdev_fini(struct bochs_device *bochs) 191{ 192 if (bochs->fb.initialized) 193 bochs_fbdev_destroy(bochs); 194 195 if (bochs->fb.helper.fbdev) 196 drm_fb_helper_fini(&bochs->fb.helper); 197 198 bochs->fb.initialized = false; 199}