at v3.10 374 lines 8.9 kB view raw
1/* 2 * Copyright (C) 2012-2013 Avionic Design GmbH 3 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 4 * 5 * Based on the KMS/FB CMA helpers 6 * Copyright (C) 2012 Analog Device Inc. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13#include <linux/module.h> 14 15#include "drm.h" 16#include "gem.h" 17 18static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb) 19{ 20 return container_of(fb, struct tegra_fb, base); 21} 22 23static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) 24{ 25 return container_of(helper, struct tegra_fbdev, base); 26} 27 28struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, 29 unsigned int index) 30{ 31 struct tegra_fb *fb = to_tegra_fb(framebuffer); 32 33 if (index >= drm_format_num_planes(framebuffer->pixel_format)) 34 return NULL; 35 36 return fb->planes[index]; 37} 38 39static void tegra_fb_destroy(struct drm_framebuffer *framebuffer) 40{ 41 struct tegra_fb *fb = to_tegra_fb(framebuffer); 42 unsigned int i; 43 44 for (i = 0; i < fb->num_planes; i++) { 45 struct tegra_bo *bo = fb->planes[i]; 46 47 if (bo) 48 drm_gem_object_unreference_unlocked(&bo->gem); 49 } 50 51 drm_framebuffer_cleanup(framebuffer); 52 kfree(fb->planes); 53 kfree(fb); 54} 55 56static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer, 57 struct drm_file *file, unsigned int *handle) 58{ 59 struct tegra_fb *fb = to_tegra_fb(framebuffer); 60 61 return drm_gem_handle_create(file, &fb->planes[0]->gem, handle); 62} 63 64static struct drm_framebuffer_funcs tegra_fb_funcs = { 65 .destroy = tegra_fb_destroy, 66 .create_handle = tegra_fb_create_handle, 67}; 68 69static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, 70 struct drm_mode_fb_cmd2 *mode_cmd, 71 struct tegra_bo **planes, 72 unsigned int num_planes) 73{ 74 struct tegra_fb *fb; 75 unsigned int i; 76 int err; 77 78 fb = kzalloc(sizeof(*fb), GFP_KERNEL); 79 if (!fb) 80 return ERR_PTR(-ENOMEM); 81 82 fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL); 83 if (!fb->planes) 84 return ERR_PTR(-ENOMEM); 85 86 fb->num_planes = num_planes; 87 88 drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); 89 90 for (i = 0; i < fb->num_planes; i++) 91 fb->planes[i] = planes[i]; 92 93 err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs); 94 if (err < 0) { 95 dev_err(drm->dev, "failed to initialize framebuffer: %d\n", 96 err); 97 kfree(fb->planes); 98 kfree(fb); 99 return ERR_PTR(err); 100 } 101 102 return fb; 103} 104 105static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, 106 struct drm_file *file, 107 struct drm_mode_fb_cmd2 *cmd) 108{ 109 unsigned int hsub, vsub, i; 110 struct tegra_bo *planes[4]; 111 struct drm_gem_object *gem; 112 struct tegra_fb *fb; 113 int err; 114 115 hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); 116 vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format); 117 118 for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) { 119 unsigned int width = cmd->width / (i ? hsub : 1); 120 unsigned int height = cmd->height / (i ? vsub : 1); 121 unsigned int size, bpp; 122 123 gem = drm_gem_object_lookup(drm, file, cmd->handles[i]); 124 if (!gem) { 125 err = -ENXIO; 126 goto unreference; 127 } 128 129 bpp = drm_format_plane_cpp(cmd->pixel_format, i); 130 131 size = (height - 1) * cmd->pitches[i] + 132 width * bpp + cmd->offsets[i]; 133 134 if (gem->size < size) { 135 err = -EINVAL; 136 goto unreference; 137 } 138 139 planes[i] = to_tegra_bo(gem); 140 } 141 142 fb = tegra_fb_alloc(drm, cmd, planes, i); 143 if (IS_ERR(fb)) { 144 err = PTR_ERR(fb); 145 goto unreference; 146 } 147 148 return &fb->base; 149 150unreference: 151 while (i--) 152 drm_gem_object_unreference_unlocked(&planes[i]->gem); 153 154 return ERR_PTR(err); 155} 156 157static struct fb_ops tegra_fb_ops = { 158 .owner = THIS_MODULE, 159 .fb_fillrect = sys_fillrect, 160 .fb_copyarea = sys_copyarea, 161 .fb_imageblit = sys_imageblit, 162 .fb_check_var = drm_fb_helper_check_var, 163 .fb_set_par = drm_fb_helper_set_par, 164 .fb_blank = drm_fb_helper_blank, 165 .fb_pan_display = drm_fb_helper_pan_display, 166 .fb_setcmap = drm_fb_helper_setcmap, 167}; 168 169static int tegra_fbdev_probe(struct drm_fb_helper *helper, 170 struct drm_fb_helper_surface_size *sizes) 171{ 172 struct tegra_fbdev *fbdev = to_tegra_fbdev(helper); 173 struct drm_device *drm = helper->dev; 174 struct drm_mode_fb_cmd2 cmd = { 0 }; 175 unsigned int bytes_per_pixel; 176 struct drm_framebuffer *fb; 177 unsigned long offset; 178 struct fb_info *info; 179 struct tegra_bo *bo; 180 size_t size; 181 int err; 182 183 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 184 185 cmd.width = sizes->surface_width; 186 cmd.height = sizes->surface_height; 187 cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; 188 cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 189 sizes->surface_depth); 190 191 size = cmd.pitches[0] * cmd.height; 192 193 bo = tegra_bo_create(drm, size); 194 if (IS_ERR(bo)) 195 return PTR_ERR(bo); 196 197 info = framebuffer_alloc(0, drm->dev); 198 if (!info) { 199 dev_err(drm->dev, "failed to allocate framebuffer info\n"); 200 tegra_bo_free_object(&bo->gem); 201 return -ENOMEM; 202 } 203 204 fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1); 205 if (IS_ERR(fbdev->fb)) { 206 dev_err(drm->dev, "failed to allocate DRM framebuffer\n"); 207 err = PTR_ERR(fbdev->fb); 208 goto release; 209 } 210 211 fb = &fbdev->fb->base; 212 helper->fb = fb; 213 helper->fbdev = info; 214 215 info->par = helper; 216 info->flags = FBINFO_FLAG_DEFAULT; 217 info->fbops = &tegra_fb_ops; 218 219 err = fb_alloc_cmap(&info->cmap, 256, 0); 220 if (err < 0) { 221 dev_err(drm->dev, "failed to allocate color map: %d\n", err); 222 goto destroy; 223 } 224 225 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); 226 drm_fb_helper_fill_var(info, helper, fb->width, fb->height); 227 228 offset = info->var.xoffset * bytes_per_pixel + 229 info->var.yoffset * fb->pitches[0]; 230 231 drm->mode_config.fb_base = (resource_size_t)bo->paddr; 232 info->screen_base = bo->vaddr + offset; 233 info->screen_size = size; 234 info->fix.smem_start = (unsigned long)(bo->paddr + offset); 235 info->fix.smem_len = size; 236 237 return 0; 238 239destroy: 240 drm_framebuffer_unregister_private(fb); 241 tegra_fb_destroy(fb); 242release: 243 framebuffer_release(info); 244 return err; 245} 246 247static struct drm_fb_helper_funcs tegra_fb_helper_funcs = { 248 .fb_probe = tegra_fbdev_probe, 249}; 250 251static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, 252 unsigned int preferred_bpp, 253 unsigned int num_crtc, 254 unsigned int max_connectors) 255{ 256 struct drm_fb_helper *helper; 257 struct tegra_fbdev *fbdev; 258 int err; 259 260 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); 261 if (!fbdev) { 262 dev_err(drm->dev, "failed to allocate DRM fbdev\n"); 263 return ERR_PTR(-ENOMEM); 264 } 265 266 fbdev->base.funcs = &tegra_fb_helper_funcs; 267 helper = &fbdev->base; 268 269 err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); 270 if (err < 0) { 271 dev_err(drm->dev, "failed to initialize DRM FB helper\n"); 272 goto free; 273 } 274 275 err = drm_fb_helper_single_add_all_connectors(&fbdev->base); 276 if (err < 0) { 277 dev_err(drm->dev, "failed to add connectors\n"); 278 goto fini; 279 } 280 281 drm_helper_disable_unused_functions(drm); 282 283 err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp); 284 if (err < 0) { 285 dev_err(drm->dev, "failed to set initial configuration\n"); 286 goto fini; 287 } 288 289 return fbdev; 290 291fini: 292 drm_fb_helper_fini(&fbdev->base); 293free: 294 kfree(fbdev); 295 return ERR_PTR(err); 296} 297 298static void tegra_fbdev_free(struct tegra_fbdev *fbdev) 299{ 300 struct fb_info *info = fbdev->base.fbdev; 301 302 if (info) { 303 int err; 304 305 err = unregister_framebuffer(info); 306 if (err < 0) 307 DRM_DEBUG_KMS("failed to unregister framebuffer\n"); 308 309 if (info->cmap.len) 310 fb_dealloc_cmap(&info->cmap); 311 312 framebuffer_release(info); 313 } 314 315 if (fbdev->fb) { 316 drm_framebuffer_unregister_private(&fbdev->fb->base); 317 tegra_fb_destroy(&fbdev->fb->base); 318 } 319 320 drm_fb_helper_fini(&fbdev->base); 321 kfree(fbdev); 322} 323 324static void tegra_fb_output_poll_changed(struct drm_device *drm) 325{ 326 struct host1x_drm *host1x = drm->dev_private; 327 328 if (host1x->fbdev) 329 drm_fb_helper_hotplug_event(&host1x->fbdev->base); 330} 331 332static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { 333 .fb_create = tegra_fb_create, 334 .output_poll_changed = tegra_fb_output_poll_changed, 335}; 336 337int tegra_drm_fb_init(struct drm_device *drm) 338{ 339 struct host1x_drm *host1x = drm->dev_private; 340 struct tegra_fbdev *fbdev; 341 342 drm->mode_config.min_width = 0; 343 drm->mode_config.min_height = 0; 344 345 drm->mode_config.max_width = 4096; 346 drm->mode_config.max_height = 4096; 347 348 drm->mode_config.funcs = &tegra_drm_mode_funcs; 349 350 fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc, 351 drm->mode_config.num_connector); 352 if (IS_ERR(fbdev)) 353 return PTR_ERR(fbdev); 354 355 host1x->fbdev = fbdev; 356 357 return 0; 358} 359 360void tegra_drm_fb_exit(struct drm_device *drm) 361{ 362 struct host1x_drm *host1x = drm->dev_private; 363 364 tegra_fbdev_free(host1x->fbdev); 365} 366 367void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) 368{ 369 if (fbdev) { 370 drm_modeset_lock_all(fbdev->base.dev); 371 drm_fb_helper_restore_fbdev_mode(&fbdev->base); 372 drm_modeset_unlock_all(fbdev->base.dev); 373 } 374}