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