aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/host1x
diff options
context:
space:
mode:
authorArto Merilainen <amerilainen@nvidia.com>2013-03-22 10:34:08 -0400
committerThierry Reding <thierry.reding@avionic-design.de>2013-04-22 06:40:04 -0400
commitde2ba664c30fcdb0f678ab6cbb57e01a0b206085 (patch)
treeff5cca80336ae30f9d050305750a89332d4198a8 /drivers/gpu/host1x
parent692e6d7be8099225f04b2d97299bc03479a5fcdb (diff)
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>
Diffstat (limited to 'drivers/gpu/host1x')
-rw-r--r--drivers/gpu/host1x/Makefile1
-rw-r--r--drivers/gpu/host1x/drm/Kconfig8
-rw-r--r--drivers/gpu/host1x/drm/dc.c23
-rw-r--r--drivers/gpu/host1x/drm/drm.c17
-rw-r--r--drivers/gpu/host1x/drm/drm.h18
-rw-r--r--drivers/gpu/host1x/drm/fb.c338
-rw-r--r--drivers/gpu/host1x/drm/gem.c270
-rw-r--r--drivers/gpu/host1x/drm/gem.h59
8 files changed, 700 insertions, 34 deletions
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index 9a6fc767f6b8..3768dbc27184 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -15,4 +15,5 @@ ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
15 15
16host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o 16host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o
17host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o 17host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o
18host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o
18obj-$(CONFIG_TEGRA_HOST1X) += host1x.o 19obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
diff --git a/drivers/gpu/host1x/drm/Kconfig b/drivers/gpu/host1x/drm/Kconfig
index 33f8f7a39196..9a28901d07ce 100644
--- a/drivers/gpu/host1x/drm/Kconfig
+++ b/drivers/gpu/host1x/drm/Kconfig
@@ -2,11 +2,9 @@ config DRM_TEGRA
2 bool "NVIDIA Tegra DRM" 2 bool "NVIDIA Tegra DRM"
3 depends on DRM && OF 3 depends on DRM && OF
4 select DRM_KMS_HELPER 4 select DRM_KMS_HELPER
5 select DRM_GEM_CMA_HELPER 5 select FB_SYS_FILLRECT
6 select DRM_KMS_CMA_HELPER 6 select FB_SYS_COPYAREA
7 select FB_CFB_FILLRECT 7 select FB_SYS_IMAGEBLIT
8 select FB_CFB_COPYAREA
9 select FB_CFB_IMAGEBLIT
10 help 8 help
11 Choose this option if you have an NVIDIA Tegra SoC. 9 Choose this option if you have an NVIDIA Tegra SoC.
12 10
diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c
index 29a79b66f36c..85ea6163a7b0 100644
--- a/drivers/gpu/host1x/drm/dc.c
+++ b/drivers/gpu/host1x/drm/dc.c
@@ -14,9 +14,10 @@
14#include <linux/platform_device.h> 14#include <linux/platform_device.h>
15#include <linux/clk/tegra.h> 15#include <linux/clk/tegra.h>
16 16
17#include "drm.h"
18#include "dc.h"
19#include "host1x_client.h" 17#include "host1x_client.h"
18#include "dc.h"
19#include "drm.h"
20#include "gem.h"
20 21
21struct tegra_plane { 22struct tegra_plane {
22 struct drm_plane base; 23 struct drm_plane base;
@@ -52,9 +53,9 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
52 window.bits_per_pixel = fb->bits_per_pixel; 53 window.bits_per_pixel = fb->bits_per_pixel;
53 54
54 for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { 55 for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
55 struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); 56 struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
56 57
57 window.base[i] = gem->paddr + fb->offsets[i]; 58 window.base[i] = bo->paddr + fb->offsets[i];
58 59
59 /* 60 /*
60 * Tegra doesn't support different strides for U and V planes 61 * Tegra doesn't support different strides for U and V planes
@@ -137,7 +138,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
137static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, 138static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
138 struct drm_framebuffer *fb) 139 struct drm_framebuffer *fb)
139{ 140{
140 struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, 0); 141 struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
141 unsigned long value; 142 unsigned long value;
142 143
143 tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); 144 tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -145,7 +146,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
145 value = fb->offsets[0] + y * fb->pitches[0] + 146 value = fb->offsets[0] + y * fb->pitches[0] +
146 x * fb->bits_per_pixel / 8; 147 x * fb->bits_per_pixel / 8;
147 148
148 tegra_dc_writel(dc, gem->paddr + value, DC_WINBUF_START_ADDR); 149 tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
149 tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); 150 tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
150 151
151 value = GENERAL_UPDATE | WIN_A_UPDATE; 152 value = GENERAL_UPDATE | WIN_A_UPDATE;
@@ -187,20 +188,20 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
187{ 188{
188 struct drm_device *drm = dc->base.dev; 189 struct drm_device *drm = dc->base.dev;
189 struct drm_crtc *crtc = &dc->base; 190 struct drm_crtc *crtc = &dc->base;
190 struct drm_gem_cma_object *gem;
191 unsigned long flags, base; 191 unsigned long flags, base;
192 struct tegra_bo *bo;
192 193
193 if (!dc->event) 194 if (!dc->event)
194 return; 195 return;
195 196
196 gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); 197 bo = tegra_fb_get_plane(crtc->fb, 0);
197 198
198 /* check if new start address has been latched */ 199 /* check if new start address has been latched */
199 tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 200 tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
200 base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); 201 base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
201 tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); 202 tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
202 203
203 if (base == gem->paddr + crtc->fb->offsets[0]) { 204 if (base == bo->paddr + crtc->fb->offsets[0]) {
204 spin_lock_irqsave(&drm->event_lock, flags); 205 spin_lock_irqsave(&drm->event_lock, flags);
205 drm_send_vblank_event(drm, dc->pipe, dc->event); 206 drm_send_vblank_event(drm, dc->pipe, dc->event);
206 drm_vblank_put(drm, dc->pipe); 207 drm_vblank_put(drm, dc->pipe);
@@ -570,7 +571,7 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
570 struct drm_display_mode *adjusted, 571 struct drm_display_mode *adjusted,
571 int x, int y, struct drm_framebuffer *old_fb) 572 int x, int y, struct drm_framebuffer *old_fb)
572{ 573{
573 struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); 574 struct tegra_bo *bo = tegra_fb_get_plane(crtc->fb, 0);
574 struct tegra_dc *dc = to_tegra_dc(crtc); 575 struct tegra_dc *dc = to_tegra_dc(crtc);
575 struct tegra_dc_window window; 576 struct tegra_dc_window window;
576 unsigned long div, value; 577 unsigned long div, value;
@@ -617,7 +618,7 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
617 window.format = tegra_dc_format(crtc->fb->pixel_format); 618 window.format = tegra_dc_format(crtc->fb->pixel_format);
618 window.bits_per_pixel = crtc->fb->bits_per_pixel; 619 window.bits_per_pixel = crtc->fb->bits_per_pixel;
619 window.stride[0] = crtc->fb->pitches[0]; 620 window.stride[0] = crtc->fb->pitches[0];
620 window.base[0] = gem->paddr; 621 window.base[0] = bo->paddr;
621 622
622 err = tegra_dc_setup_window(dc, 0, &window); 623 err = tegra_dc_setup_window(dc, 0, &window);
623 if (err < 0) 624 if (err < 0)
diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c
index 901f0b47815c..c4e45c16f991 100644
--- a/drivers/gpu/host1x/drm/drm.c
+++ b/drivers/gpu/host1x/drm/drm.c
@@ -15,7 +15,10 @@
15#include <asm/dma-iommu.h> 15#include <asm/dma-iommu.h>
16 16
17#include "host1x_client.h" 17#include "host1x_client.h"
18#include "dev.h"
18#include "drm.h" 19#include "drm.h"
20#include "gem.h"
21#include "syncpt.h"
19 22
20#define DRIVER_NAME "tegra" 23#define DRIVER_NAME "tegra"
21#define DRIVER_DESC "NVIDIA Tegra graphics" 24#define DRIVER_DESC "NVIDIA Tegra graphics"
@@ -281,7 +284,7 @@ static void tegra_drm_lastclose(struct drm_device *drm)
281{ 284{
282 struct host1x_drm *host1x = drm->dev_private; 285 struct host1x_drm *host1x = drm->dev_private;
283 286
284 drm_fbdev_cma_restore_mode(host1x->fbdev); 287 tegra_fbdev_restore_mode(host1x->fbdev);
285} 288}
286 289
287static struct drm_ioctl_desc tegra_drm_ioctls[] = { 290static struct drm_ioctl_desc tegra_drm_ioctls[] = {
@@ -292,7 +295,7 @@ static const struct file_operations tegra_drm_fops = {
292 .open = drm_open, 295 .open = drm_open,
293 .release = drm_release, 296 .release = drm_release,
294 .unlocked_ioctl = drm_ioctl, 297 .unlocked_ioctl = drm_ioctl,
295 .mmap = drm_gem_cma_mmap, 298 .mmap = tegra_drm_mmap,
296 .poll = drm_poll, 299 .poll = drm_poll,
297 .fasync = drm_fasync, 300 .fasync = drm_fasync,
298 .read = drm_read, 301 .read = drm_read,
@@ -408,11 +411,11 @@ struct drm_driver tegra_drm_driver = {
408 .debugfs_cleanup = tegra_debugfs_cleanup, 411 .debugfs_cleanup = tegra_debugfs_cleanup,
409#endif 412#endif
410 413
411 .gem_free_object = drm_gem_cma_free_object, 414 .gem_free_object = tegra_bo_free_object,
412 .gem_vm_ops = &drm_gem_cma_vm_ops, 415 .gem_vm_ops = &tegra_bo_vm_ops,
413 .dumb_create = drm_gem_cma_dumb_create, 416 .dumb_create = tegra_bo_dumb_create,
414 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 417 .dumb_map_offset = tegra_bo_dumb_map_offset,
415 .dumb_destroy = drm_gem_cma_dumb_destroy, 418 .dumb_destroy = tegra_bo_dumb_destroy,
416 419
417 .ioctls = tegra_drm_ioctls, 420 .ioctls = tegra_drm_ioctls,
418 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), 421 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h
index 0b8738fc444c..3864a39f8ad3 100644
--- a/drivers/gpu/host1x/drm/drm.h
+++ b/drivers/gpu/host1x/drm/drm.h
@@ -14,10 +14,19 @@
14#include <drm/drm_crtc_helper.h> 14#include <drm/drm_crtc_helper.h>
15#include <drm/drm_edid.h> 15#include <drm/drm_edid.h>
16#include <drm/drm_fb_helper.h> 16#include <drm/drm_fb_helper.h>
17#include <drm/drm_gem_cma_helper.h>
18#include <drm/drm_fb_cma_helper.h>
19#include <drm/drm_fixed.h> 17#include <drm/drm_fixed.h>
20 18
19struct tegra_fb {
20 struct drm_framebuffer base;
21 struct tegra_bo **planes;
22 unsigned int num_planes;
23};
24
25struct tegra_fbdev {
26 struct drm_fb_helper base;
27 struct tegra_fb *fb;
28};
29
21struct host1x_drm { 30struct host1x_drm {
22 struct drm_device *drm; 31 struct drm_device *drm;
23 struct device *dev; 32 struct device *dev;
@@ -33,7 +42,7 @@ struct host1x_drm {
33 struct mutex clients_lock; 42 struct mutex clients_lock;
34 struct list_head clients; 43 struct list_head clients;
35 44
36 struct drm_fbdev_cma *fbdev; 45 struct tegra_fbdev *fbdev;
37}; 46};
38 47
39struct host1x_client; 48struct host1x_client;
@@ -226,8 +235,11 @@ extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output
226extern int tegra_output_exit(struct tegra_output *output); 235extern int tegra_output_exit(struct tegra_output *output);
227 236
228/* from fb.c */ 237/* from fb.c */
238struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
239 unsigned int index);
229extern int tegra_drm_fb_init(struct drm_device *drm); 240extern int tegra_drm_fb_init(struct drm_device *drm);
230extern void tegra_drm_fb_exit(struct drm_device *drm); 241extern void tegra_drm_fb_exit(struct drm_device *drm);
242extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
231 243
232extern struct drm_driver tegra_drm_driver; 244extern struct drm_driver tegra_drm_driver;
233 245
diff --git a/drivers/gpu/host1x/drm/fb.c b/drivers/gpu/host1x/drm/fb.c
index 6ed885aead48..979a3e32b78b 100644
--- a/drivers/gpu/host1x/drm/fb.c
+++ b/drivers/gpu/host1x/drm/fb.c
@@ -1,30 +1,343 @@
1/* 1/*
2 * Copyright (C) 2012 Avionic Design GmbH 2 * Copyright (C) 2012-2013 Avionic Design GmbH
3 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 3 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
4 * 4 *
5 * Based on the KMS/FB CMA helpers
6 * Copyright (C) 2012 Analog Device Inc.
7 *
5 * This program is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as 9 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation. 10 * published by the Free Software Foundation.
8 */ 11 */
9 12
13#include <linux/module.h>
14
10#include "drm.h" 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);
11 153
12static void tegra_drm_fb_output_poll_changed(struct drm_device *drm) 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)
13{ 325{
14 struct host1x_drm *host1x = drm->dev_private; 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
19static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { 332static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
20 .fb_create = drm_fb_cma_create, 333 .fb_create = tegra_fb_create,
21 .output_poll_changed = tegra_drm_fb_output_poll_changed, 334 .output_poll_changed = tegra_fb_output_poll_changed,
22}; 335};
23 336
24int tegra_drm_fb_init(struct drm_device *drm) 337int tegra_drm_fb_init(struct drm_device *drm)
25{ 338{
26 struct host1x_drm *host1x = drm->dev_private; 339 struct host1x_drm *host1x = drm->dev_private;
27 struct drm_fbdev_cma *fbdev; 340 struct tegra_fbdev *fbdev;
28 341
29 drm->mode_config.min_width = 0; 342 drm->mode_config.min_width = 0;
30 drm->mode_config.min_height = 0; 343 drm->mode_config.min_height = 0;
@@ -34,7 +347,7 @@ int tegra_drm_fb_init(struct drm_device *drm)
34 347
35 drm->mode_config.funcs = &tegra_drm_mode_funcs; 348 drm->mode_config.funcs = &tegra_drm_mode_funcs;
36 349
37 fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, 350 fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
38 drm->mode_config.num_connector); 351 drm->mode_config.num_connector);
39 if (IS_ERR(fbdev)) 352 if (IS_ERR(fbdev))
40 return PTR_ERR(fbdev); 353 return PTR_ERR(fbdev);
@@ -48,5 +361,14 @@ void tegra_drm_fb_exit(struct drm_device *drm)
48{ 361{
49 struct host1x_drm *host1x = drm->dev_private; 362 struct host1x_drm *host1x = drm->dev_private;
50 363
51 drm_fbdev_cma_fini(host1x->fbdev); 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 }
52} 374}
diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
new file mode 100644
index 000000000000..c5e9a9b494c2
--- /dev/null
+++ b/drivers/gpu/host1x/drm/gem.c
@@ -0,0 +1,270 @@
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
32static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo)
33{
34 return container_of(bo, struct tegra_bo, base);
35}
36
37static 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
47static 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
54static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
55{
56}
57
58static 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
65static void tegra_bo_munmap(struct host1x_bo *bo, void *addr)
66{
67}
68
69static 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
76static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page,
77 void *addr)
78{
79}
80
81static 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
93const 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
104static 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
109unsigned 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
114struct 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
145err_mmap:
146 drm_gem_object_release(&bo->gem);
147err_init:
148 tegra_bo_destroy(drm, bo);
149err_dma:
150 kfree(bo);
151
152 return ERR_PTR(err);
153
154}
155
156struct 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
176err:
177 tegra_bo_free_object(&bo->gem);
178 return ERR_PTR(ret);
179}
180
181void 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
194int 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
214int 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
240const struct vm_operations_struct tegra_bo_vm_ops = {
241 .open = drm_gem_vm_open,
242 .close = drm_gem_vm_close,
243};
244
245int 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
266int 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}
diff --git a/drivers/gpu/host1x/drm/gem.h b/drivers/gpu/host1x/drm/gem.h
new file mode 100644
index 000000000000..34de2b486eb7
--- /dev/null
+++ b/drivers/gpu/host1x/drm/gem.h
@@ -0,0 +1,59 @@
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
27struct tegra_bo {
28 struct drm_gem_object gem;
29 struct host1x_bo base;
30 dma_addr_t paddr;
31 void *vaddr;
32};
33
34static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem)
35{
36 return container_of(gem, struct tegra_bo, gem);
37}
38
39extern const struct host1x_bo_ops tegra_bo_ops;
40
41struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size);
42struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
43 struct drm_device *drm,
44 unsigned int size,
45 unsigned int *handle);
46void tegra_bo_free_object(struct drm_gem_object *gem);
47unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo);
48int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
49 struct drm_mode_create_dumb *args);
50int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm,
51 uint32_t handle, uint64_t *offset);
52int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm,
53 unsigned int handle);
54
55int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma);
56
57extern const struct vm_operations_struct tegra_bo_vm_ops;
58
59#endif