Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: host1x: drm: Add memory manager and fb

This patch introduces a memory manager for tegra drm and moves
existing parts to use it. As cma framebuffer helpers can no more
be used, this patch adds also a separate framebuffer driver for
tegra.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Erik Faye-Lund <kusmabite@gmail.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>

authored by

Arto Merilainen and committed by
Thierry Reding
de2ba664 692e6d7b

+701 -35
+1
drivers/gpu/host1x/Makefile
··· 15 15 16 16 host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o 17 17 host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o 18 + host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o 18 19 obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
+3 -5
drivers/gpu/host1x/drm/Kconfig
··· 2 2 bool "NVIDIA Tegra DRM" 3 3 depends on DRM && OF 4 4 select DRM_KMS_HELPER 5 - select DRM_GEM_CMA_HELPER 6 - select DRM_KMS_CMA_HELPER 7 - select FB_CFB_FILLRECT 8 - select FB_CFB_COPYAREA 9 - select FB_CFB_IMAGEBLIT 5 + select FB_SYS_FILLRECT 6 + select FB_SYS_COPYAREA 7 + select FB_SYS_IMAGEBLIT 10 8 help 11 9 Choose this option if you have an NVIDIA Tegra SoC. 12 10
+12 -11
drivers/gpu/host1x/drm/dc.c
··· 14 14 #include <linux/platform_device.h> 15 15 #include <linux/clk/tegra.h> 16 16 17 - #include "drm.h" 18 - #include "dc.h" 19 17 #include "host1x_client.h" 18 + #include "dc.h" 19 + #include "drm.h" 20 + #include "gem.h" 20 21 21 22 struct tegra_plane { 22 23 struct drm_plane base; ··· 53 52 window.bits_per_pixel = fb->bits_per_pixel; 54 53 55 54 for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { 56 - struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); 55 + struct tegra_bo *bo = tegra_fb_get_plane(fb, i); 57 56 58 - window.base[i] = gem->paddr + fb->offsets[i]; 57 + window.base[i] = bo->paddr + fb->offsets[i]; 59 58 60 59 /* 61 60 * Tegra doesn't support different strides for U and V planes ··· 138 137 static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, 139 138 struct drm_framebuffer *fb) 140 139 { 141 - struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, 0); 140 + struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); 142 141 unsigned long value; 143 142 144 143 tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); ··· 146 145 value = fb->offsets[0] + y * fb->pitches[0] + 147 146 x * fb->bits_per_pixel / 8; 148 147 149 - tegra_dc_writel(dc, gem->paddr + value, DC_WINBUF_START_ADDR); 148 + tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); 150 149 tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); 151 150 152 151 value = GENERAL_UPDATE | WIN_A_UPDATE; ··· 188 187 { 189 188 struct drm_device *drm = dc->base.dev; 190 189 struct drm_crtc *crtc = &dc->base; 191 - struct drm_gem_cma_object *gem; 192 190 unsigned long flags, base; 191 + struct tegra_bo *bo; 193 192 194 193 if (!dc->event) 195 194 return; 196 195 197 - gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); 196 + bo = tegra_fb_get_plane(crtc->fb, 0); 198 197 199 198 /* check if new start address has been latched */ 200 199 tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 201 200 base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); 202 201 tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); 203 202 204 - if (base == gem->paddr + crtc->fb->offsets[0]) { 203 + if (base == bo->paddr + crtc->fb->offsets[0]) { 205 204 spin_lock_irqsave(&drm->event_lock, flags); 206 205 drm_send_vblank_event(drm, dc->pipe, dc->event); 207 206 drm_vblank_put(drm, dc->pipe); ··· 571 570 struct drm_display_mode *adjusted, 572 571 int x, int y, struct drm_framebuffer *old_fb) 573 572 { 574 - struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); 573 + struct tegra_bo *bo = tegra_fb_get_plane(crtc->fb, 0); 575 574 struct tegra_dc *dc = to_tegra_dc(crtc); 576 575 struct tegra_dc_window window; 577 576 unsigned long div, value; ··· 618 617 window.format = tegra_dc_format(crtc->fb->pixel_format); 619 618 window.bits_per_pixel = crtc->fb->bits_per_pixel; 620 619 window.stride[0] = crtc->fb->pitches[0]; 621 - window.base[0] = gem->paddr; 620 + window.base[0] = bo->paddr; 622 621 623 622 err = tegra_dc_setup_window(dc, 0, &window); 624 623 if (err < 0)
+10 -7
drivers/gpu/host1x/drm/drm.c
··· 15 15 #include <asm/dma-iommu.h> 16 16 17 17 #include "host1x_client.h" 18 + #include "dev.h" 18 19 #include "drm.h" 20 + #include "gem.h" 21 + #include "syncpt.h" 19 22 20 23 #define DRIVER_NAME "tegra" 21 24 #define DRIVER_DESC "NVIDIA Tegra graphics" ··· 284 281 { 285 282 struct host1x_drm *host1x = drm->dev_private; 286 283 287 - drm_fbdev_cma_restore_mode(host1x->fbdev); 284 + tegra_fbdev_restore_mode(host1x->fbdev); 288 285 } 289 286 290 287 static struct drm_ioctl_desc tegra_drm_ioctls[] = { ··· 295 292 .open = drm_open, 296 293 .release = drm_release, 297 294 .unlocked_ioctl = drm_ioctl, 298 - .mmap = drm_gem_cma_mmap, 295 + .mmap = tegra_drm_mmap, 299 296 .poll = drm_poll, 300 297 .fasync = drm_fasync, 301 298 .read = drm_read, ··· 411 408 .debugfs_cleanup = tegra_debugfs_cleanup, 412 409 #endif 413 410 414 - .gem_free_object = drm_gem_cma_free_object, 415 - .gem_vm_ops = &drm_gem_cma_vm_ops, 416 - .dumb_create = drm_gem_cma_dumb_create, 417 - .dumb_map_offset = drm_gem_cma_dumb_map_offset, 418 - .dumb_destroy = drm_gem_cma_dumb_destroy, 411 + .gem_free_object = tegra_bo_free_object, 412 + .gem_vm_ops = &tegra_bo_vm_ops, 413 + .dumb_create = tegra_bo_dumb_create, 414 + .dumb_map_offset = tegra_bo_dumb_map_offset, 415 + .dumb_destroy = tegra_bo_dumb_destroy, 419 416 420 417 .ioctls = tegra_drm_ioctls, 421 418 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
+15 -3
drivers/gpu/host1x/drm/drm.h
··· 14 14 #include <drm/drm_crtc_helper.h> 15 15 #include <drm/drm_edid.h> 16 16 #include <drm/drm_fb_helper.h> 17 - #include <drm/drm_gem_cma_helper.h> 18 - #include <drm/drm_fb_cma_helper.h> 19 17 #include <drm/drm_fixed.h> 18 + 19 + struct tegra_fb { 20 + struct drm_framebuffer base; 21 + struct tegra_bo **planes; 22 + unsigned int num_planes; 23 + }; 24 + 25 + struct tegra_fbdev { 26 + struct drm_fb_helper base; 27 + struct tegra_fb *fb; 28 + }; 20 29 21 30 struct host1x_drm { 22 31 struct drm_device *drm; ··· 42 33 struct mutex clients_lock; 43 34 struct list_head clients; 44 35 45 - struct drm_fbdev_cma *fbdev; 36 + struct tegra_fbdev *fbdev; 46 37 }; 47 38 48 39 struct host1x_client; ··· 235 226 extern int tegra_output_exit(struct tegra_output *output); 236 227 237 228 /* from fb.c */ 229 + struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, 230 + unsigned int index); 238 231 extern int tegra_drm_fb_init(struct drm_device *drm); 239 232 extern void tegra_drm_fb_exit(struct drm_device *drm); 233 + extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); 240 234 241 235 extern struct drm_driver tegra_drm_driver; 242 236
+331 -9
drivers/gpu/host1x/drm/fb.c
··· 1 1 /* 2 - * Copyright (C) 2012 Avionic Design GmbH 2 + * Copyright (C) 2012-2013 Avionic Design GmbH 3 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. 4 7 * 5 8 * This program is free software; you can redistribute it and/or modify 6 9 * it under the terms of the GNU General Public License version 2 as 7 10 * published by the Free Software Foundation. 8 11 */ 9 12 10 - #include "drm.h" 13 + #include <linux/module.h> 11 14 12 - static void tegra_drm_fb_output_poll_changed(struct drm_device *drm) 15 + #include "drm.h" 16 + #include "gem.h" 17 + 18 + static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb) 19 + { 20 + return container_of(fb, struct tegra_fb, base); 21 + } 22 + 23 + static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) 24 + { 25 + return container_of(helper, struct tegra_fbdev, base); 26 + } 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 + static 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 + 56 + static 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 + 64 + static struct drm_framebuffer_funcs tegra_fb_funcs = { 65 + .destroy = tegra_fb_destroy, 66 + .create_handle = tegra_fb_create_handle, 67 + }; 68 + 69 + static 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 + 105 + static 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 + 150 + unreference: 151 + while (i--) 152 + drm_gem_object_unreference_unlocked(&planes[i]->gem); 153 + 154 + return ERR_PTR(err); 155 + } 156 + 157 + static 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 + 169 + static 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 + 239 + destroy: 240 + drm_framebuffer_unregister_private(fb); 241 + tegra_fb_destroy(fb); 242 + release: 243 + framebuffer_release(info); 244 + return err; 245 + } 246 + 247 + static struct drm_fb_helper_funcs tegra_fb_helper_funcs = { 248 + .fb_probe = tegra_fbdev_probe, 249 + }; 250 + 251 + static 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 + 291 + fini: 292 + drm_fb_helper_fini(&fbdev->base); 293 + free: 294 + kfree(fbdev); 295 + return ERR_PTR(err); 296 + } 297 + 298 + static 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 + 324 + static void tegra_fb_output_poll_changed(struct drm_device *drm) 13 325 { 14 326 struct host1x_drm *host1x = drm->dev_private; 15 327 16 - drm_fbdev_cma_hotplug_event(host1x->fbdev); 328 + if (host1x->fbdev) 329 + drm_fb_helper_hotplug_event(&host1x->fbdev->base); 17 330 } 18 331 19 332 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { 20 - .fb_create = drm_fb_cma_create, 21 - .output_poll_changed = tegra_drm_fb_output_poll_changed, 333 + .fb_create = tegra_fb_create, 334 + .output_poll_changed = tegra_fb_output_poll_changed, 22 335 }; 23 336 24 337 int tegra_drm_fb_init(struct drm_device *drm) 25 338 { 26 339 struct host1x_drm *host1x = drm->dev_private; 27 - struct drm_fbdev_cma *fbdev; 340 + struct tegra_fbdev *fbdev; 28 341 29 342 drm->mode_config.min_width = 0; 30 343 drm->mode_config.min_height = 0; ··· 347 34 348 35 drm->mode_config.funcs = &tegra_drm_mode_funcs; 349 36 350 - fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, 37 + fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc, 351 38 drm->mode_config.num_connector); 352 39 if (IS_ERR(fbdev)) 353 40 return PTR_ERR(fbdev); ··· 361 48 { 362 49 struct host1x_drm *host1x = drm->dev_private; 363 50 364 - drm_fbdev_cma_fini(host1x->fbdev); 51 + tegra_fbdev_free(host1x->fbdev); 52 + } 53 + 54 + void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) 55 + { 56 + if (fbdev) { 57 + drm_modeset_lock_all(fbdev->base.dev); 58 + drm_fb_helper_restore_fbdev_mode(&fbdev->base); 59 + drm_modeset_unlock_all(fbdev->base.dev); 60 + } 365 61 }
+270
drivers/gpu/host1x/drm/gem.c
··· 1 + /* 2 + * NVIDIA Tegra DRM GEM helper functions 3 + * 4 + * Copyright (C) 2012 Sascha Hauer, Pengutronix 5 + * Copyright (C) 2013 NVIDIA CORPORATION, All rights reserved. 6 + * 7 + * Based on the GEM/CMA helpers 8 + * 9 + * Copyright (c) 2011 Samsung Electronics Co., Ltd. 10 + * 11 + * This program is free software; you can redistribute it and/or 12 + * modify it under the terms of the GNU General Public License 13 + * as published by the Free Software Foundation; either version 2 14 + * of the License, or (at your option) any later version. 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 + * GNU General Public License for more details. 19 + */ 20 + 21 + #include <linux/mm.h> 22 + #include <linux/slab.h> 23 + #include <linux/mutex.h> 24 + #include <linux/export.h> 25 + #include <linux/dma-mapping.h> 26 + 27 + #include <drm/drmP.h> 28 + #include <drm/drm.h> 29 + 30 + #include "gem.h" 31 + 32 + static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo) 33 + { 34 + return container_of(bo, struct tegra_bo, base); 35 + } 36 + 37 + static void tegra_bo_put(struct host1x_bo *bo) 38 + { 39 + struct tegra_bo *obj = host1x_to_drm_bo(bo); 40 + struct drm_device *drm = obj->gem.dev; 41 + 42 + mutex_lock(&drm->struct_mutex); 43 + drm_gem_object_unreference(&obj->gem); 44 + mutex_unlock(&drm->struct_mutex); 45 + } 46 + 47 + static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt) 48 + { 49 + struct tegra_bo *obj = host1x_to_drm_bo(bo); 50 + 51 + return obj->paddr; 52 + } 53 + 54 + static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt) 55 + { 56 + } 57 + 58 + static void *tegra_bo_mmap(struct host1x_bo *bo) 59 + { 60 + struct tegra_bo *obj = host1x_to_drm_bo(bo); 61 + 62 + return obj->vaddr; 63 + } 64 + 65 + static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) 66 + { 67 + } 68 + 69 + static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) 70 + { 71 + struct tegra_bo *obj = host1x_to_drm_bo(bo); 72 + 73 + return obj->vaddr + page * PAGE_SIZE; 74 + } 75 + 76 + static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, 77 + void *addr) 78 + { 79 + } 80 + 81 + static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) 82 + { 83 + struct tegra_bo *obj = host1x_to_drm_bo(bo); 84 + struct drm_device *drm = obj->gem.dev; 85 + 86 + mutex_lock(&drm->struct_mutex); 87 + drm_gem_object_reference(&obj->gem); 88 + mutex_unlock(&drm->struct_mutex); 89 + 90 + return bo; 91 + } 92 + 93 + const struct host1x_bo_ops tegra_bo_ops = { 94 + .get = tegra_bo_get, 95 + .put = tegra_bo_put, 96 + .pin = tegra_bo_pin, 97 + .unpin = tegra_bo_unpin, 98 + .mmap = tegra_bo_mmap, 99 + .munmap = tegra_bo_munmap, 100 + .kmap = tegra_bo_kmap, 101 + .kunmap = tegra_bo_kunmap, 102 + }; 103 + 104 + static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) 105 + { 106 + dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr); 107 + } 108 + 109 + unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo) 110 + { 111 + return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT; 112 + } 113 + 114 + struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size) 115 + { 116 + struct tegra_bo *bo; 117 + int err; 118 + 119 + bo = kzalloc(sizeof(*bo), GFP_KERNEL); 120 + if (!bo) 121 + return ERR_PTR(-ENOMEM); 122 + 123 + host1x_bo_init(&bo->base, &tegra_bo_ops); 124 + size = round_up(size, PAGE_SIZE); 125 + 126 + bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, 127 + GFP_KERNEL | __GFP_NOWARN); 128 + if (!bo->vaddr) { 129 + dev_err(drm->dev, "failed to allocate buffer with size %u\n", 130 + size); 131 + err = -ENOMEM; 132 + goto err_dma; 133 + } 134 + 135 + err = drm_gem_object_init(drm, &bo->gem, size); 136 + if (err) 137 + goto err_init; 138 + 139 + err = drm_gem_create_mmap_offset(&bo->gem); 140 + if (err) 141 + goto err_mmap; 142 + 143 + return bo; 144 + 145 + err_mmap: 146 + drm_gem_object_release(&bo->gem); 147 + err_init: 148 + tegra_bo_destroy(drm, bo); 149 + err_dma: 150 + kfree(bo); 151 + 152 + return ERR_PTR(err); 153 + 154 + } 155 + 156 + struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, 157 + struct drm_device *drm, 158 + unsigned int size, 159 + unsigned int *handle) 160 + { 161 + struct tegra_bo *bo; 162 + int ret; 163 + 164 + bo = tegra_bo_create(drm, size); 165 + if (IS_ERR(bo)) 166 + return bo; 167 + 168 + ret = drm_gem_handle_create(file, &bo->gem, handle); 169 + if (ret) 170 + goto err; 171 + 172 + drm_gem_object_unreference_unlocked(&bo->gem); 173 + 174 + return bo; 175 + 176 + err: 177 + tegra_bo_free_object(&bo->gem); 178 + return ERR_PTR(ret); 179 + } 180 + 181 + void tegra_bo_free_object(struct drm_gem_object *gem) 182 + { 183 + struct tegra_bo *bo = to_tegra_bo(gem); 184 + 185 + if (gem->map_list.map) 186 + drm_gem_free_mmap_offset(gem); 187 + 188 + drm_gem_object_release(gem); 189 + tegra_bo_destroy(gem->dev, bo); 190 + 191 + kfree(bo); 192 + } 193 + 194 + int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, 195 + struct drm_mode_create_dumb *args) 196 + { 197 + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 198 + struct tegra_bo *bo; 199 + 200 + if (args->pitch < min_pitch) 201 + args->pitch = min_pitch; 202 + 203 + if (args->size < args->pitch * args->height) 204 + args->size = args->pitch * args->height; 205 + 206 + bo = tegra_bo_create_with_handle(file, drm, args->size, 207 + &args->handle); 208 + if (IS_ERR(bo)) 209 + return PTR_ERR(bo); 210 + 211 + return 0; 212 + } 213 + 214 + int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, 215 + uint32_t handle, uint64_t *offset) 216 + { 217 + struct drm_gem_object *gem; 218 + struct tegra_bo *bo; 219 + 220 + mutex_lock(&drm->struct_mutex); 221 + 222 + gem = drm_gem_object_lookup(drm, file, handle); 223 + if (!gem) { 224 + dev_err(drm->dev, "failed to lookup GEM object\n"); 225 + mutex_unlock(&drm->struct_mutex); 226 + return -EINVAL; 227 + } 228 + 229 + bo = to_tegra_bo(gem); 230 + 231 + *offset = tegra_bo_get_mmap_offset(bo); 232 + 233 + drm_gem_object_unreference(gem); 234 + 235 + mutex_unlock(&drm->struct_mutex); 236 + 237 + return 0; 238 + } 239 + 240 + const struct vm_operations_struct tegra_bo_vm_ops = { 241 + .open = drm_gem_vm_open, 242 + .close = drm_gem_vm_close, 243 + }; 244 + 245 + int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) 246 + { 247 + struct drm_gem_object *gem; 248 + struct tegra_bo *bo; 249 + int ret; 250 + 251 + ret = drm_gem_mmap(file, vma); 252 + if (ret) 253 + return ret; 254 + 255 + gem = vma->vm_private_data; 256 + bo = to_tegra_bo(gem); 257 + 258 + ret = remap_pfn_range(vma, vma->vm_start, bo->paddr >> PAGE_SHIFT, 259 + vma->vm_end - vma->vm_start, vma->vm_page_prot); 260 + if (ret) 261 + drm_gem_vm_close(vma); 262 + 263 + return ret; 264 + } 265 + 266 + int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, 267 + unsigned int handle) 268 + { 269 + return drm_gem_handle_delete(file, handle); 270 + }
+59
drivers/gpu/host1x/drm/gem.h
··· 1 + /* 2 + * Tegra host1x GEM implementation 3 + * 4 + * Copyright (c) 2012-2013, NVIDIA Corporation. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope it will be useful, but WITHOUT 11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 + * more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 + */ 18 + 19 + #ifndef __HOST1X_GEM_H 20 + #define __HOST1X_GEM_H 21 + 22 + #include <drm/drm.h> 23 + #include <drm/drmP.h> 24 + 25 + #include "host1x_bo.h" 26 + 27 + struct tegra_bo { 28 + struct drm_gem_object gem; 29 + struct host1x_bo base; 30 + dma_addr_t paddr; 31 + void *vaddr; 32 + }; 33 + 34 + static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem) 35 + { 36 + return container_of(gem, struct tegra_bo, gem); 37 + } 38 + 39 + extern const struct host1x_bo_ops tegra_bo_ops; 40 + 41 + struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size); 42 + struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, 43 + struct drm_device *drm, 44 + unsigned int size, 45 + unsigned int *handle); 46 + void tegra_bo_free_object(struct drm_gem_object *gem); 47 + unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo); 48 + int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, 49 + struct drm_mode_create_dumb *args); 50 + int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, 51 + uint32_t handle, uint64_t *offset); 52 + int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, 53 + unsigned int handle); 54 + 55 + int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); 56 + 57 + extern const struct vm_operations_struct tegra_bo_vm_ops; 58 + 59 + #endif