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 .fb_check_var = drm_fb_helper_check_var, 26 .fb_set_par = drm_fb_helper_set_par, 27 .fb_fillrect = sys_fillrect, 28 .fb_copyarea = sys_copyarea, 29 .fb_imageblit = sys_imageblit, 30 .fb_pan_display = drm_fb_helper_pan_display, 31 .fb_blank = drm_fb_helper_blank, 32 .fb_setcmap = drm_fb_helper_setcmap, 33 .fb_mmap = bochsfb_mmap, 34}; 35 36static int bochsfb_create_object(struct bochs_device *bochs, 37 struct drm_mode_fb_cmd2 *mode_cmd, 38 struct drm_gem_object **gobj_p) 39{ 40 struct drm_device *dev = bochs->dev; 41 struct drm_gem_object *gobj; 42 u32 size; 43 int ret = 0; 44 45 size = mode_cmd->pitches[0] * mode_cmd->height; 46 ret = bochs_gem_create(dev, size, true, &gobj); 47 if (ret) 48 return ret; 49 50 *gobj_p = gobj; 51 return ret; 52} 53 54static int bochsfb_create(struct drm_fb_helper *helper, 55 struct drm_fb_helper_surface_size *sizes) 56{ 57 struct bochs_device *bochs = 58 container_of(helper, struct bochs_device, fb.helper); 59 struct drm_device *dev = bochs->dev; 60 struct fb_info *info; 61 struct drm_framebuffer *fb; 62 struct drm_mode_fb_cmd2 mode_cmd; 63 struct device *device = &dev->pdev->dev; 64 struct drm_gem_object *gobj = NULL; 65 struct bochs_bo *bo = NULL; 66 int size, ret; 67 68 if (sizes->surface_bpp != 32) 69 return -EINVAL; 70 71 mode_cmd.width = sizes->surface_width; 72 mode_cmd.height = sizes->surface_height; 73 mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); 74 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 75 sizes->surface_depth); 76 size = mode_cmd.pitches[0] * mode_cmd.height; 77 78 /* alloc, pin & map bo */ 79 ret = bochsfb_create_object(bochs, &mode_cmd, &gobj); 80 if (ret) { 81 DRM_ERROR("failed to create fbcon backing object %d\n", ret); 82 return ret; 83 } 84 85 bo = gem_to_bochs_bo(gobj); 86 87 ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL); 88 if (ret) 89 return ret; 90 91 ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL); 92 if (ret) { 93 DRM_ERROR("failed to pin fbcon\n"); 94 ttm_bo_unreserve(&bo->bo); 95 return ret; 96 } 97 98 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, 99 &bo->kmap); 100 if (ret) { 101 DRM_ERROR("failed to kmap fbcon\n"); 102 ttm_bo_unreserve(&bo->bo); 103 return ret; 104 } 105 106 ttm_bo_unreserve(&bo->bo); 107 108 /* init fb device */ 109 info = framebuffer_alloc(0, device); 110 if (info == NULL) 111 return -ENOMEM; 112 113 info->par = &bochs->fb.helper; 114 115 ret = bochs_framebuffer_init(bochs->dev, &bochs->fb.gfb, &mode_cmd, gobj); 116 if (ret) 117 return ret; 118 119 bochs->fb.size = size; 120 121 /* setup helper */ 122 fb = &bochs->fb.gfb.base; 123 bochs->fb.helper.fb = fb; 124 bochs->fb.helper.fbdev = info; 125 126 strcpy(info->fix.id, "bochsdrmfb"); 127 128 info->flags = FBINFO_DEFAULT; 129 info->fbops = &bochsfb_ops; 130 131 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); 132 drm_fb_helper_fill_var(info, &bochs->fb.helper, sizes->fb_width, 133 sizes->fb_height); 134 135 info->screen_base = bo->kmap.virtual; 136 info->screen_size = size; 137 138 drm_vma_offset_remove(&bo->bo.bdev->vma_manager, &bo->bo.vma_node); 139 info->fix.smem_start = 0; 140 info->fix.smem_len = size; 141 142 ret = fb_alloc_cmap(&info->cmap, 256, 0); 143 if (ret) { 144 DRM_ERROR("%s: can't allocate color map\n", info->fix.id); 145 return -ENOMEM; 146 } 147 148 return 0; 149} 150 151static int bochs_fbdev_destroy(struct bochs_device *bochs) 152{ 153 struct bochs_framebuffer *gfb = &bochs->fb.gfb; 154 struct fb_info *info; 155 156 DRM_DEBUG_DRIVER("\n"); 157 158 if (bochs->fb.helper.fbdev) { 159 info = bochs->fb.helper.fbdev; 160 161 unregister_framebuffer(info); 162 if (info->cmap.len) 163 fb_dealloc_cmap(&info->cmap); 164 framebuffer_release(info); 165 } 166 167 if (gfb->obj) { 168 drm_gem_object_unreference_unlocked(gfb->obj); 169 gfb->obj = NULL; 170 } 171 172 drm_fb_helper_fini(&bochs->fb.helper); 173 drm_framebuffer_unregister_private(&gfb->base); 174 drm_framebuffer_cleanup(&gfb->base); 175 176 return 0; 177} 178 179void bochs_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, 180 u16 blue, int regno) 181{ 182} 183 184void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, 185 u16 *blue, int regno) 186{ 187 *red = regno; 188 *green = regno; 189 *blue = regno; 190} 191 192static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = { 193 .gamma_set = bochs_fb_gamma_set, 194 .gamma_get = bochs_fb_gamma_get, 195 .fb_probe = bochsfb_create, 196}; 197 198int bochs_fbdev_init(struct bochs_device *bochs) 199{ 200 int ret; 201 202 drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, 203 &bochs_fb_helper_funcs); 204 205 ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 206 1, 1); 207 if (ret) 208 return ret; 209 210 ret = drm_fb_helper_single_add_all_connectors(&bochs->fb.helper); 211 if (ret) 212 goto fini; 213 214 drm_helper_disable_unused_functions(bochs->dev); 215 216 ret = drm_fb_helper_initial_config(&bochs->fb.helper, 32); 217 if (ret) 218 goto fini; 219 220 bochs->fb.initialized = true; 221 return 0; 222 223fini: 224 drm_fb_helper_fini(&bochs->fb.helper); 225 return ret; 226} 227 228void bochs_fbdev_fini(struct bochs_device *bochs) 229{ 230 if (!bochs->fb.initialized) 231 return; 232 233 bochs_fbdev_destroy(bochs); 234 bochs->fb.initialized = false; 235} 236