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