aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-02-11 12:43:09 -0500
committerRob Clark <robdclark@gmail.com>2013-02-16 17:38:06 -0500
commit8bb0daffb0b8e45188066255b4203446eae181f1 (patch)
treec1a324b863df57becdfab54c9325231bbb853b56 /drivers/gpu
parenta4462f246c8821f625f45bce52c7ca7e0207dffe (diff)
drm/omap: move out of staging
Now that the omapdss interface has been reworked so that omapdrm can use dispc directly, we have been able to fix the remaining functional kms issues with omapdrm. And in the mean time the PM sequencing and many other of that open issues have been solved. So I think it makes sense to finally move omapdrm out of staging. Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/omapdrm/Kconfig25
-rw-r--r--drivers/gpu/drm/omapdrm/Makefile24
-rw-r--r--drivers/gpu/drm/omapdrm/TODO23
-rw-r--r--drivers/gpu/drm/omapdrm/omap_connector.c298
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c659
-rw-r--r--drivers/gpu/drm/omapdrm/omap_debugfs.c139
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_priv.h188
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c991
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.h141
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c610
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.h333
-rw-r--r--drivers/gpu/drm/omapdrm/omap_encoder.c170
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c472
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c402
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c1511
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c225
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem_helpers.c169
-rw-r--r--drivers/gpu/drm/omapdrm/omap_irq.c322
-rw-r--r--drivers/gpu/drm/omapdrm/omap_plane.c450
-rw-r--r--drivers/gpu/drm/omapdrm/tcm-sita.c703
-rw-r--r--drivers/gpu/drm/omapdrm/tcm-sita.h95
-rw-r--r--drivers/gpu/drm/omapdrm/tcm.h328
24 files changed, 8281 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index ed9e3af17b31..0ce5f52ac56e 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -215,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig"
215source "drivers/gpu/drm/shmobile/Kconfig" 215source "drivers/gpu/drm/shmobile/Kconfig"
216 216
217source "drivers/gpu/drm/tegra/Kconfig" 217source "drivers/gpu/drm/tegra/Kconfig"
218
219source "drivers/gpu/drm/omapdrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 6f58c81cfcbc..b6b43cbc18e4 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/
50obj-$(CONFIG_DRM_AST) += ast/ 50obj-$(CONFIG_DRM_AST) += ast/
51obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ 51obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
52obj-$(CONFIG_DRM_TEGRA) += tegra/ 52obj-$(CONFIG_DRM_TEGRA) += tegra/
53obj-$(CONFIG_DRM_OMAP) += omapdrm/
53obj-y += i2c/ 54obj-y += i2c/
diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig
new file mode 100644
index 000000000000..b724a4131435
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/Kconfig
@@ -0,0 +1,25 @@
1
2config DRM_OMAP
3 tristate "OMAP DRM"
4 depends on DRM && !CONFIG_FB_OMAP2
5 depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
6 select DRM_KMS_HELPER
7 select OMAP2_DSS
8 select FB_SYS_FILLRECT
9 select FB_SYS_COPYAREA
10 select FB_SYS_IMAGEBLIT
11 select FB_SYS_FOPS
12 default n
13 help
14 DRM display driver for OMAP2/3/4 based boards.
15
16config DRM_OMAP_NUM_CRTCS
17 int "Number of CRTCs"
18 range 1 10
19 default 1 if ARCH_OMAP2 || ARCH_OMAP3
20 default 2 if ARCH_OMAP4
21 depends on DRM_OMAP
22 help
23 Select the number of video overlays which can be used as framebuffers.
24 The remaining overlays are reserved for video.
25
diff --git a/drivers/gpu/drm/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile
new file mode 100644
index 000000000000..d85e058f2845
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/Makefile
@@ -0,0 +1,24 @@
1#
2# Makefile for the drm device driver. This driver provides support for the
3# Direct Rendering Infrastructure (DRI)
4#
5
6ccflags-y := -Iinclude/drm -Werror
7omapdrm-y := omap_drv.o \
8 omap_irq.o \
9 omap_debugfs.o \
10 omap_crtc.o \
11 omap_plane.o \
12 omap_encoder.o \
13 omap_connector.o \
14 omap_fb.o \
15 omap_fbdev.o \
16 omap_gem.o \
17 omap_gem_dmabuf.o \
18 omap_dmm_tiler.o \
19 tcm-sita.o
20
21# temporary:
22omapdrm-y += omap_gem_helpers.o
23
24obj-$(CONFIG_DRM_OMAP) += omapdrm.o
diff --git a/drivers/gpu/drm/omapdrm/TODO b/drivers/gpu/drm/omapdrm/TODO
new file mode 100644
index 000000000000..4d8c18aa5dd7
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/TODO
@@ -0,0 +1,23 @@
1TODO
2. Where should we do eviction (detatch_pages())? We aren't necessarily
3 accessing the pages via a GART, so maybe we need some other threshold
4 to put a cap on the # of pages that can be pin'd.
5 . Use mm_shrinker to trigger unpinning pages.
6 . This is mainly theoretical since most of these devices don't actually
7 have swap or harddrive.
8. GEM/shmem backed pages can have existing mappings (kernel linear map,
9 etc..), which isn't really ideal.
10. Revisit GEM sync object infrastructure.. TTM has some framework for this
11 already. Possibly this could be refactored out and made more common?
12 There should be some way to do this with less wheel-reinvention.
13 . This can be handled by the dma-buf fence/reservation stuff when it
14 lands
15
16Userspace:
17. git://anongit.freedesktop.org/xorg/driver/xf86-video-omap
18
19Currently tested on
20. OMAP3530 beagleboard
21. OMAP4430 pandaboard
22. OMAP4460 pandaboard
23. OMAP5432 uEVM
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
new file mode 100644
index 000000000000..44284fd981fc
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -0,0 +1,298 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_connector.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include "drm_crtc.h"
23#include "drm_crtc_helper.h"
24
25/*
26 * connector funcs
27 */
28
29#define to_omap_connector(x) container_of(x, struct omap_connector, base)
30
31struct omap_connector {
32 struct drm_connector base;
33 struct omap_dss_device *dssdev;
34 struct drm_encoder *encoder;
35};
36
37void copy_timings_omap_to_drm(struct drm_display_mode *mode,
38 struct omap_video_timings *timings)
39{
40 mode->clock = timings->pixel_clock;
41
42 mode->hdisplay = timings->x_res;
43 mode->hsync_start = mode->hdisplay + timings->hfp;
44 mode->hsync_end = mode->hsync_start + timings->hsw;
45 mode->htotal = mode->hsync_end + timings->hbp;
46
47 mode->vdisplay = timings->y_res;
48 mode->vsync_start = mode->vdisplay + timings->vfp;
49 mode->vsync_end = mode->vsync_start + timings->vsw;
50 mode->vtotal = mode->vsync_end + timings->vbp;
51
52 mode->flags = 0;
53
54 if (timings->interlace)
55 mode->flags |= DRM_MODE_FLAG_INTERLACE;
56
57 if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
58 mode->flags |= DRM_MODE_FLAG_PHSYNC;
59 else
60 mode->flags |= DRM_MODE_FLAG_NHSYNC;
61
62 if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
63 mode->flags |= DRM_MODE_FLAG_PVSYNC;
64 else
65 mode->flags |= DRM_MODE_FLAG_NVSYNC;
66}
67
68void copy_timings_drm_to_omap(struct omap_video_timings *timings,
69 struct drm_display_mode *mode)
70{
71 timings->pixel_clock = mode->clock;
72
73 timings->x_res = mode->hdisplay;
74 timings->hfp = mode->hsync_start - mode->hdisplay;
75 timings->hsw = mode->hsync_end - mode->hsync_start;
76 timings->hbp = mode->htotal - mode->hsync_end;
77
78 timings->y_res = mode->vdisplay;
79 timings->vfp = mode->vsync_start - mode->vdisplay;
80 timings->vsw = mode->vsync_end - mode->vsync_start;
81 timings->vbp = mode->vtotal - mode->vsync_end;
82
83 timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
84
85 if (mode->flags & DRM_MODE_FLAG_PHSYNC)
86 timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
87 else
88 timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW;
89
90 if (mode->flags & DRM_MODE_FLAG_PVSYNC)
91 timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
92 else
93 timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW;
94
95 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
96 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
97 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
98}
99
100static enum drm_connector_status omap_connector_detect(
101 struct drm_connector *connector, bool force)
102{
103 struct omap_connector *omap_connector = to_omap_connector(connector);
104 struct omap_dss_device *dssdev = omap_connector->dssdev;
105 struct omap_dss_driver *dssdrv = dssdev->driver;
106 enum drm_connector_status ret;
107
108 if (dssdrv->detect) {
109 if (dssdrv->detect(dssdev))
110 ret = connector_status_connected;
111 else
112 ret = connector_status_disconnected;
113 } else {
114 ret = connector_status_unknown;
115 }
116
117 VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
118
119 return ret;
120}
121
122static void omap_connector_destroy(struct drm_connector *connector)
123{
124 struct omap_connector *omap_connector = to_omap_connector(connector);
125 struct omap_dss_device *dssdev = omap_connector->dssdev;
126
127 DBG("%s", omap_connector->dssdev->name);
128 drm_sysfs_connector_remove(connector);
129 drm_connector_cleanup(connector);
130 kfree(omap_connector);
131
132 omap_dss_put_device(dssdev);
133}
134
135#define MAX_EDID 512
136
137static int omap_connector_get_modes(struct drm_connector *connector)
138{
139 struct omap_connector *omap_connector = to_omap_connector(connector);
140 struct omap_dss_device *dssdev = omap_connector->dssdev;
141 struct omap_dss_driver *dssdrv = dssdev->driver;
142 struct drm_device *dev = connector->dev;
143 int n = 0;
144
145 DBG("%s", omap_connector->dssdev->name);
146
147 /* if display exposes EDID, then we parse that in the normal way to
148 * build table of supported modes.. otherwise (ie. fixed resolution
149 * LCD panels) we just return a single mode corresponding to the
150 * currently configured timings:
151 */
152 if (dssdrv->read_edid) {
153 void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
154
155 if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) &&
156 drm_edid_is_valid(edid)) {
157 drm_mode_connector_update_edid_property(
158 connector, edid);
159 n = drm_add_edid_modes(connector, edid);
160 } else {
161 drm_mode_connector_update_edid_property(
162 connector, NULL);
163 }
164 kfree(edid);
165 } else {
166 struct drm_display_mode *mode = drm_mode_create(dev);
167 struct omap_video_timings timings = {0};
168
169 dssdrv->get_timings(dssdev, &timings);
170
171 copy_timings_omap_to_drm(mode, &timings);
172
173 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
174 drm_mode_set_name(mode);
175 drm_mode_probed_add(connector, mode);
176
177 n = 1;
178 }
179
180 return n;
181}
182
183static int omap_connector_mode_valid(struct drm_connector *connector,
184 struct drm_display_mode *mode)
185{
186 struct omap_connector *omap_connector = to_omap_connector(connector);
187 struct omap_dss_device *dssdev = omap_connector->dssdev;
188 struct omap_dss_driver *dssdrv = dssdev->driver;
189 struct omap_video_timings timings = {0};
190 struct drm_device *dev = connector->dev;
191 struct drm_display_mode *new_mode;
192 int ret = MODE_BAD;
193
194 copy_timings_drm_to_omap(&timings, mode);
195 mode->vrefresh = drm_mode_vrefresh(mode);
196
197 if (!dssdrv->check_timings(dssdev, &timings)) {
198 /* check if vrefresh is still valid */
199 new_mode = drm_mode_duplicate(dev, mode);
200 new_mode->clock = timings.pixel_clock;
201 new_mode->vrefresh = 0;
202 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
203 ret = MODE_OK;
204 drm_mode_destroy(dev, new_mode);
205 }
206
207 DBG("connector: mode %s: "
208 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
209 (ret == MODE_OK) ? "valid" : "invalid",
210 mode->base.id, mode->name, mode->vrefresh, mode->clock,
211 mode->hdisplay, mode->hsync_start,
212 mode->hsync_end, mode->htotal,
213 mode->vdisplay, mode->vsync_start,
214 mode->vsync_end, mode->vtotal, mode->type, mode->flags);
215
216 return ret;
217}
218
219struct drm_encoder *omap_connector_attached_encoder(
220 struct drm_connector *connector)
221{
222 struct omap_connector *omap_connector = to_omap_connector(connector);
223 return omap_connector->encoder;
224}
225
226static const struct drm_connector_funcs omap_connector_funcs = {
227 .dpms = drm_helper_connector_dpms,
228 .detect = omap_connector_detect,
229 .fill_modes = drm_helper_probe_single_connector_modes,
230 .destroy = omap_connector_destroy,
231};
232
233static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
234 .get_modes = omap_connector_get_modes,
235 .mode_valid = omap_connector_mode_valid,
236 .best_encoder = omap_connector_attached_encoder,
237};
238
239/* flush an area of the framebuffer (in case of manual update display that
240 * is not automatically flushed)
241 */
242void omap_connector_flush(struct drm_connector *connector,
243 int x, int y, int w, int h)
244{
245 struct omap_connector *omap_connector = to_omap_connector(connector);
246
247 /* TODO: enable when supported in dss */
248 VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
249}
250
251/* initialize connector */
252struct drm_connector *omap_connector_init(struct drm_device *dev,
253 int connector_type, struct omap_dss_device *dssdev,
254 struct drm_encoder *encoder)
255{
256 struct drm_connector *connector = NULL;
257 struct omap_connector *omap_connector;
258
259 DBG("%s", dssdev->name);
260
261 omap_dss_get_device(dssdev);
262
263 omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
264 if (!omap_connector) {
265 dev_err(dev->dev, "could not allocate connector\n");
266 goto fail;
267 }
268
269 omap_connector->dssdev = dssdev;
270 omap_connector->encoder = encoder;
271
272 connector = &omap_connector->base;
273
274 drm_connector_init(dev, connector, &omap_connector_funcs,
275 connector_type);
276 drm_connector_helper_add(connector, &omap_connector_helper_funcs);
277
278#if 0 /* enable when dss2 supports hotplug */
279 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD)
280 connector->polled = 0;
281 else
282#endif
283 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
284 DRM_CONNECTOR_POLL_DISCONNECT;
285
286 connector->interlace_allowed = 1;
287 connector->doublescan_allowed = 0;
288
289 drm_sysfs_connector_add(connector);
290
291 return connector;
292
293fail:
294 if (connector)
295 omap_connector_destroy(connector);
296
297 return NULL;
298}
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
new file mode 100644
index 000000000000..2b97cf90071b
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -0,0 +1,659 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_crtc.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include <drm/drm_mode.h>
23#include "drm_crtc.h"
24#include "drm_crtc_helper.h"
25
26#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
27
28struct omap_crtc {
29 struct drm_crtc base;
30 struct drm_plane *plane;
31
32 const char *name;
33 int pipe;
34 enum omap_channel channel;
35 struct omap_overlay_manager_info info;
36
37 /*
38 * Temporary: eventually this will go away, but it is needed
39 * for now to keep the output's happy. (They only need
40 * mgr->id.) Eventually this will be replaced w/ something
41 * more common-panel-framework-y
42 */
43 struct omap_overlay_manager mgr;
44
45 struct omap_video_timings timings;
46 bool enabled;
47 bool full_update;
48
49 struct omap_drm_apply apply;
50
51 struct omap_drm_irq apply_irq;
52 struct omap_drm_irq error_irq;
53
54 /* list of in-progress apply's: */
55 struct list_head pending_applies;
56
57 /* list of queued apply's: */
58 struct list_head queued_applies;
59
60 /* for handling queued and in-progress applies: */
61 struct work_struct apply_work;
62
63 /* if there is a pending flip, these will be non-null: */
64 struct drm_pending_vblank_event *event;
65 struct drm_framebuffer *old_fb;
66
67 /* for handling page flips without caring about what
68 * the callback is called from. Possibly we should just
69 * make omap_gem always call the cb from the worker so
70 * we don't have to care about this..
71 *
72 * XXX maybe fold into apply_work??
73 */
74 struct work_struct page_flip_work;
75};
76
77/*
78 * Manager-ops, callbacks from output when they need to configure
79 * the upstream part of the video pipe.
80 *
81 * Most of these we can ignore until we add support for command-mode
82 * panels.. for video-mode the crtc-helpers already do an adequate
83 * job of sequencing the setup of the video pipe in the proper order
84 */
85
86/* we can probably ignore these until we support command-mode panels: */
87static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
88{
89}
90
91static int omap_crtc_enable(struct omap_overlay_manager *mgr)
92{
93 return 0;
94}
95
96static void omap_crtc_disable(struct omap_overlay_manager *mgr)
97{
98}
99
100static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
101 const struct omap_video_timings *timings)
102{
103 struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
104 DBG("%s", omap_crtc->name);
105 omap_crtc->timings = *timings;
106 omap_crtc->full_update = true;
107}
108
109static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
110 const struct dss_lcd_mgr_config *config)
111{
112 struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
113 DBG("%s", omap_crtc->name);
114 dispc_mgr_set_lcd_config(omap_crtc->channel, config);
115}
116
117static int omap_crtc_register_framedone_handler(
118 struct omap_overlay_manager *mgr,
119 void (*handler)(void *), void *data)
120{
121 return 0;
122}
123
124static void omap_crtc_unregister_framedone_handler(
125 struct omap_overlay_manager *mgr,
126 void (*handler)(void *), void *data)
127{
128}
129
130static const struct dss_mgr_ops mgr_ops = {
131 .start_update = omap_crtc_start_update,
132 .enable = omap_crtc_enable,
133 .disable = omap_crtc_disable,
134 .set_timings = omap_crtc_set_timings,
135 .set_lcd_config = omap_crtc_set_lcd_config,
136 .register_framedone_handler = omap_crtc_register_framedone_handler,
137 .unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
138};
139
140/*
141 * CRTC funcs:
142 */
143
144static void omap_crtc_destroy(struct drm_crtc *crtc)
145{
146 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
147
148 DBG("%s", omap_crtc->name);
149
150 WARN_ON(omap_crtc->apply_irq.registered);
151 omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
152
153 omap_crtc->plane->funcs->destroy(omap_crtc->plane);
154 drm_crtc_cleanup(crtc);
155
156 kfree(omap_crtc);
157}
158
159static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
160{
161 struct omap_drm_private *priv = crtc->dev->dev_private;
162 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
163 bool enabled = (mode == DRM_MODE_DPMS_ON);
164 int i;
165
166 DBG("%s: %d", omap_crtc->name, mode);
167
168 if (enabled != omap_crtc->enabled) {
169 omap_crtc->enabled = enabled;
170 omap_crtc->full_update = true;
171 omap_crtc_apply(crtc, &omap_crtc->apply);
172
173 /* also enable our private plane: */
174 WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
175
176 /* and any attached overlay planes: */
177 for (i = 0; i < priv->num_planes; i++) {
178 struct drm_plane *plane = priv->planes[i];
179 if (plane->crtc == crtc)
180 WARN_ON(omap_plane_dpms(plane, mode));
181 }
182 }
183}
184
185static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
186 const struct drm_display_mode *mode,
187 struct drm_display_mode *adjusted_mode)
188{
189 return true;
190}
191
192static int omap_crtc_mode_set(struct drm_crtc *crtc,
193 struct drm_display_mode *mode,
194 struct drm_display_mode *adjusted_mode,
195 int x, int y,
196 struct drm_framebuffer *old_fb)
197{
198 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
199
200 mode = adjusted_mode;
201
202 DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
203 omap_crtc->name, mode->base.id, mode->name,
204 mode->vrefresh, mode->clock,
205 mode->hdisplay, mode->hsync_start,
206 mode->hsync_end, mode->htotal,
207 mode->vdisplay, mode->vsync_start,
208 mode->vsync_end, mode->vtotal,
209 mode->type, mode->flags);
210
211 copy_timings_drm_to_omap(&omap_crtc->timings, mode);
212 omap_crtc->full_update = true;
213
214 return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
215 0, 0, mode->hdisplay, mode->vdisplay,
216 x << 16, y << 16,
217 mode->hdisplay << 16, mode->vdisplay << 16,
218 NULL, NULL);
219}
220
221static void omap_crtc_prepare(struct drm_crtc *crtc)
222{
223 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
224 DBG("%s", omap_crtc->name);
225 omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
226}
227
228static void omap_crtc_commit(struct drm_crtc *crtc)
229{
230 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
231 DBG("%s", omap_crtc->name);
232 omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
233}
234
235static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
236 struct drm_framebuffer *old_fb)
237{
238 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
239 struct drm_plane *plane = omap_crtc->plane;
240 struct drm_display_mode *mode = &crtc->mode;
241
242 return omap_plane_mode_set(plane, crtc, crtc->fb,
243 0, 0, mode->hdisplay, mode->vdisplay,
244 x << 16, y << 16,
245 mode->hdisplay << 16, mode->vdisplay << 16,
246 NULL, NULL);
247}
248
249static void omap_crtc_load_lut(struct drm_crtc *crtc)
250{
251}
252
253static void vblank_cb(void *arg)
254{
255 struct drm_crtc *crtc = arg;
256 struct drm_device *dev = crtc->dev;
257 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
258 unsigned long flags;
259
260 spin_lock_irqsave(&dev->event_lock, flags);
261
262 /* wakeup userspace */
263 if (omap_crtc->event)
264 drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
265
266 omap_crtc->event = NULL;
267 omap_crtc->old_fb = NULL;
268
269 spin_unlock_irqrestore(&dev->event_lock, flags);
270}
271
272static void page_flip_worker(struct work_struct *work)
273{
274 struct omap_crtc *omap_crtc =
275 container_of(work, struct omap_crtc, page_flip_work);
276 struct drm_crtc *crtc = &omap_crtc->base;
277 struct drm_device *dev = crtc->dev;
278 struct drm_display_mode *mode = &crtc->mode;
279 struct drm_gem_object *bo;
280
281 drm_modeset_lock_all(dev);
282 omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
283 0, 0, mode->hdisplay, mode->vdisplay,
284 crtc->x << 16, crtc->y << 16,
285 mode->hdisplay << 16, mode->vdisplay << 16,
286 vblank_cb, crtc);
287 drm_modeset_unlock_all(dev);
288
289 bo = omap_framebuffer_bo(crtc->fb, 0);
290 drm_gem_object_unreference_unlocked(bo);
291}
292
293static void page_flip_cb(void *arg)
294{
295 struct drm_crtc *crtc = arg;
296 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
297 struct omap_drm_private *priv = crtc->dev->dev_private;
298
299 /* avoid assumptions about what ctxt we are called from: */
300 queue_work(priv->wq, &omap_crtc->page_flip_work);
301}
302
303static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
304 struct drm_framebuffer *fb,
305 struct drm_pending_vblank_event *event)
306{
307 struct drm_device *dev = crtc->dev;
308 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
309 struct drm_gem_object *bo;
310
311 DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1,
312 fb->base.id, event);
313
314 if (omap_crtc->old_fb) {
315 dev_err(dev->dev, "already a pending flip\n");
316 return -EINVAL;
317 }
318
319 omap_crtc->event = event;
320 crtc->fb = fb;
321
322 /*
323 * Hold a reference temporarily until the crtc is updated
324 * and takes the reference to the bo. This avoids it
325 * getting freed from under us:
326 */
327 bo = omap_framebuffer_bo(fb, 0);
328 drm_gem_object_reference(bo);
329
330 omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
331
332 return 0;
333}
334
335static int omap_crtc_set_property(struct drm_crtc *crtc,
336 struct drm_property *property, uint64_t val)
337{
338 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
339 struct omap_drm_private *priv = crtc->dev->dev_private;
340
341 if (property == priv->rotation_prop) {
342 crtc->invert_dimensions =
343 !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
344 }
345
346 return omap_plane_set_property(omap_crtc->plane, property, val);
347}
348
349static const struct drm_crtc_funcs omap_crtc_funcs = {
350 .set_config = drm_crtc_helper_set_config,
351 .destroy = omap_crtc_destroy,
352 .page_flip = omap_crtc_page_flip_locked,
353 .set_property = omap_crtc_set_property,
354};
355
356static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
357 .dpms = omap_crtc_dpms,
358 .mode_fixup = omap_crtc_mode_fixup,
359 .mode_set = omap_crtc_mode_set,
360 .prepare = omap_crtc_prepare,
361 .commit = omap_crtc_commit,
362 .mode_set_base = omap_crtc_mode_set_base,
363 .load_lut = omap_crtc_load_lut,
364};
365
366const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
367{
368 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
369 return &omap_crtc->timings;
370}
371
372enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
373{
374 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
375 return omap_crtc->channel;
376}
377
378static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
379{
380 struct omap_crtc *omap_crtc =
381 container_of(irq, struct omap_crtc, error_irq);
382 struct drm_crtc *crtc = &omap_crtc->base;
383 DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
384 /* avoid getting in a flood, unregister the irq until next vblank */
385 omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
386}
387
388static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
389{
390 struct omap_crtc *omap_crtc =
391 container_of(irq, struct omap_crtc, apply_irq);
392 struct drm_crtc *crtc = &omap_crtc->base;
393
394 if (!omap_crtc->error_irq.registered)
395 omap_irq_register(crtc->dev, &omap_crtc->error_irq);
396
397 if (!dispc_mgr_go_busy(omap_crtc->channel)) {
398 struct omap_drm_private *priv =
399 crtc->dev->dev_private;
400 DBG("%s: apply done", omap_crtc->name);
401 omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
402 queue_work(priv->wq, &omap_crtc->apply_work);
403 }
404}
405
406static void apply_worker(struct work_struct *work)
407{
408 struct omap_crtc *omap_crtc =
409 container_of(work, struct omap_crtc, apply_work);
410 struct drm_crtc *crtc = &omap_crtc->base;
411 struct drm_device *dev = crtc->dev;
412 struct omap_drm_apply *apply, *n;
413 bool need_apply;
414
415 /*
416 * Synchronize everything on mode_config.mutex, to keep
417 * the callbacks and list modification all serialized
418 * with respect to modesetting ioctls from userspace.
419 */
420 drm_modeset_lock_all(dev);
421 dispc_runtime_get();
422
423 /*
424 * If we are still pending a previous update, wait.. when the
425 * pending update completes, we get kicked again.
426 */
427 if (omap_crtc->apply_irq.registered)
428 goto out;
429
430 /* finish up previous apply's: */
431 list_for_each_entry_safe(apply, n,
432 &omap_crtc->pending_applies, pending_node) {
433 apply->post_apply(apply);
434 list_del(&apply->pending_node);
435 }
436
437 need_apply = !list_empty(&omap_crtc->queued_applies);
438
439 /* then handle the next round of of queued apply's: */
440 list_for_each_entry_safe(apply, n,
441 &omap_crtc->queued_applies, queued_node) {
442 apply->pre_apply(apply);
443 list_del(&apply->queued_node);
444 apply->queued = false;
445 list_add_tail(&apply->pending_node,
446 &omap_crtc->pending_applies);
447 }
448
449 if (need_apply) {
450 enum omap_channel channel = omap_crtc->channel;
451
452 DBG("%s: GO", omap_crtc->name);
453
454 if (dispc_mgr_is_enabled(channel)) {
455 omap_irq_register(dev, &omap_crtc->apply_irq);
456 dispc_mgr_go(channel);
457 } else {
458 struct omap_drm_private *priv = dev->dev_private;
459 queue_work(priv->wq, &omap_crtc->apply_work);
460 }
461 }
462
463out:
464 dispc_runtime_put();
465 drm_modeset_unlock_all(dev);
466}
467
468int omap_crtc_apply(struct drm_crtc *crtc,
469 struct omap_drm_apply *apply)
470{
471 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
472 struct drm_device *dev = crtc->dev;
473
474 WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
475
476 /* no need to queue it again if it is already queued: */
477 if (apply->queued)
478 return 0;
479
480 apply->queued = true;
481 list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
482
483 /*
484 * If there are no currently pending updates, then go ahead and
485 * kick the worker immediately, otherwise it will run again when
486 * the current update finishes.
487 */
488 if (list_empty(&omap_crtc->pending_applies)) {
489 struct omap_drm_private *priv = crtc->dev->dev_private;
490 queue_work(priv->wq, &omap_crtc->apply_work);
491 }
492
493 return 0;
494}
495
496/* called only from apply */
497static void set_enabled(struct drm_crtc *crtc, bool enable)
498{
499 struct drm_device *dev = crtc->dev;
500 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
501 enum omap_channel channel = omap_crtc->channel;
502 struct omap_irq_wait *wait = NULL;
503
504 if (dispc_mgr_is_enabled(channel) == enable)
505 return;
506
507 /* ignore sync-lost irqs during enable/disable */
508 omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
509
510 if (dispc_mgr_get_framedone_irq(channel)) {
511 if (!enable) {
512 wait = omap_irq_wait_init(dev,
513 dispc_mgr_get_framedone_irq(channel), 1);
514 }
515 } else {
516 /*
517 * When we disable digit output, we need to wait until fields
518 * are done. Otherwise the DSS is still working, and turning
519 * off the clocks prevents DSS from going to OFF mode. And when
520 * enabling, we need to wait for the extra sync losts
521 */
522 wait = omap_irq_wait_init(dev,
523 dispc_mgr_get_vsync_irq(channel), 2);
524 }
525
526 dispc_mgr_enable(channel, enable);
527
528 if (wait) {
529 int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
530 if (ret) {
531 dev_err(dev->dev, "%s: timeout waiting for %s\n",
532 omap_crtc->name, enable ? "enable" : "disable");
533 }
534 }
535
536 omap_irq_register(crtc->dev, &omap_crtc->error_irq);
537}
538
539static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
540{
541 struct omap_crtc *omap_crtc =
542 container_of(apply, struct omap_crtc, apply);
543 struct drm_crtc *crtc = &omap_crtc->base;
544 struct drm_encoder *encoder = NULL;
545
546 DBG("%s: enabled=%d, full=%d", omap_crtc->name,
547 omap_crtc->enabled, omap_crtc->full_update);
548
549 if (omap_crtc->full_update) {
550 struct omap_drm_private *priv = crtc->dev->dev_private;
551 int i;
552 for (i = 0; i < priv->num_encoders; i++) {
553 if (priv->encoders[i]->crtc == crtc) {
554 encoder = priv->encoders[i];
555 break;
556 }
557 }
558 }
559
560 if (!omap_crtc->enabled) {
561 set_enabled(&omap_crtc->base, false);
562 if (encoder)
563 omap_encoder_set_enabled(encoder, false);
564 } else {
565 if (encoder) {
566 omap_encoder_set_enabled(encoder, false);
567 omap_encoder_update(encoder, &omap_crtc->mgr,
568 &omap_crtc->timings);
569 omap_encoder_set_enabled(encoder, true);
570 omap_crtc->full_update = false;
571 }
572
573 dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
574 dispc_mgr_set_timings(omap_crtc->channel,
575 &omap_crtc->timings);
576 set_enabled(&omap_crtc->base, true);
577 }
578
579 omap_crtc->full_update = false;
580}
581
582static void omap_crtc_post_apply(struct omap_drm_apply *apply)
583{
584 /* nothing needed for post-apply */
585}
586
587static const char *channel_names[] = {
588 [OMAP_DSS_CHANNEL_LCD] = "lcd",
589 [OMAP_DSS_CHANNEL_DIGIT] = "tv",
590 [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
591};
592
593/* initialize crtc */
594struct drm_crtc *omap_crtc_init(struct drm_device *dev,
595 struct drm_plane *plane, enum omap_channel channel, int id)
596{
597 struct drm_crtc *crtc = NULL;
598 struct omap_crtc *omap_crtc;
599 struct omap_overlay_manager_info *info;
600
601 DBG("%s", channel_names[channel]);
602
603 omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
604
605 if (!omap_crtc) {
606 dev_err(dev->dev, "could not allocate CRTC\n");
607 goto fail;
608 }
609
610 crtc = &omap_crtc->base;
611
612 INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker);
613 INIT_WORK(&omap_crtc->apply_work, apply_worker);
614
615 INIT_LIST_HEAD(&omap_crtc->pending_applies);
616 INIT_LIST_HEAD(&omap_crtc->queued_applies);
617
618 omap_crtc->apply.pre_apply = omap_crtc_pre_apply;
619 omap_crtc->apply.post_apply = omap_crtc_post_apply;
620
621 omap_crtc->apply_irq.irqmask = pipe2vbl(id);
622 omap_crtc->apply_irq.irq = omap_crtc_apply_irq;
623
624 omap_crtc->error_irq.irqmask =
625 dispc_mgr_get_sync_lost_irq(channel);
626 omap_crtc->error_irq.irq = omap_crtc_error_irq;
627 omap_irq_register(dev, &omap_crtc->error_irq);
628
629 omap_crtc->channel = channel;
630 omap_crtc->plane = plane;
631 omap_crtc->plane->crtc = crtc;
632 omap_crtc->name = channel_names[channel];
633 omap_crtc->pipe = id;
634
635 /* temporary: */
636 omap_crtc->mgr.id = channel;
637
638 dss_install_mgr_ops(&mgr_ops);
639
640 /* TODO: fix hard-coded setup.. add properties! */
641 info = &omap_crtc->info;
642 info->default_color = 0x00000000;
643 info->trans_key = 0x00000000;
644 info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
645 info->trans_enabled = false;
646
647 drm_crtc_init(dev, crtc, &omap_crtc_funcs);
648 drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
649
650 omap_plane_install_properties(omap_crtc->plane, &crtc->base);
651
652 return crtc;
653
654fail:
655 if (crtc)
656 omap_crtc_destroy(crtc);
657
658 return NULL;
659}
diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c
new file mode 100644
index 000000000000..c0aa40f8ad6a
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c
@@ -0,0 +1,139 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_debugfs.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21#include "omap_dmm_tiler.h"
22
23#include "drm_fb_helper.h"
24
25
26#ifdef CONFIG_DEBUG_FS
27
28static int gem_show(struct seq_file *m, void *arg)
29{
30 struct drm_info_node *node = (struct drm_info_node *) m->private;
31 struct drm_device *dev = node->minor->dev;
32 struct omap_drm_private *priv = dev->dev_private;
33 int ret;
34
35 ret = mutex_lock_interruptible(&dev->struct_mutex);
36 if (ret)
37 return ret;
38
39 seq_printf(m, "All Objects:\n");
40 omap_gem_describe_objects(&priv->obj_list, m);
41
42 mutex_unlock(&dev->struct_mutex);
43
44 return 0;
45}
46
47static int mm_show(struct seq_file *m, void *arg)
48{
49 struct drm_info_node *node = (struct drm_info_node *) m->private;
50 struct drm_device *dev = node->minor->dev;
51 return drm_mm_dump_table(m, dev->mm_private);
52}
53
54static int fb_show(struct seq_file *m, void *arg)
55{
56 struct drm_info_node *node = (struct drm_info_node *) m->private;
57 struct drm_device *dev = node->minor->dev;
58 struct omap_drm_private *priv = dev->dev_private;
59 struct drm_framebuffer *fb;
60 int ret;
61
62 ret = mutex_lock_interruptible(&dev->mode_config.mutex);
63 if (ret)
64 return ret;
65
66 ret = mutex_lock_interruptible(&dev->struct_mutex);
67 if (ret) {
68 mutex_unlock(&dev->mode_config.mutex);
69 return ret;
70 }
71
72 seq_printf(m, "fbcon ");
73 omap_framebuffer_describe(priv->fbdev->fb, m);
74
75 mutex_lock(&dev->mode_config.fb_lock);
76 list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
77 if (fb == priv->fbdev->fb)
78 continue;
79
80 seq_printf(m, "user ");
81 omap_framebuffer_describe(fb, m);
82 }
83 mutex_unlock(&dev->mode_config.fb_lock);
84
85 mutex_unlock(&dev->struct_mutex);
86 mutex_unlock(&dev->mode_config.mutex);
87
88 return 0;
89}
90
91/* list of debufs files that are applicable to all devices */
92static struct drm_info_list omap_debugfs_list[] = {
93 {"gem", gem_show, 0},
94 {"mm", mm_show, 0},
95 {"fb", fb_show, 0},
96};
97
98/* list of debugfs files that are specific to devices with dmm/tiler */
99static struct drm_info_list omap_dmm_debugfs_list[] = {
100 {"tiler_map", tiler_map_show, 0},
101};
102
103int omap_debugfs_init(struct drm_minor *minor)
104{
105 struct drm_device *dev = minor->dev;
106 int ret;
107
108 ret = drm_debugfs_create_files(omap_debugfs_list,
109 ARRAY_SIZE(omap_debugfs_list),
110 minor->debugfs_root, minor);
111
112 if (ret) {
113 dev_err(dev->dev, "could not install omap_debugfs_list\n");
114 return ret;
115 }
116
117 if (dmm_is_available())
118 ret = drm_debugfs_create_files(omap_dmm_debugfs_list,
119 ARRAY_SIZE(omap_dmm_debugfs_list),
120 minor->debugfs_root, minor);
121
122 if (ret) {
123 dev_err(dev->dev, "could not install omap_dmm_debugfs_list\n");
124 return ret;
125 }
126
127 return ret;
128}
129
130void omap_debugfs_cleanup(struct drm_minor *minor)
131{
132 drm_debugfs_remove_files(omap_debugfs_list,
133 ARRAY_SIZE(omap_debugfs_list), minor);
134 if (dmm_is_available())
135 drm_debugfs_remove_files(omap_dmm_debugfs_list,
136 ARRAY_SIZE(omap_dmm_debugfs_list), minor);
137}
138
139#endif
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
new file mode 100644
index 000000000000..58bcd6ae0255
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
@@ -0,0 +1,188 @@
1/*
2 *
3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Rob Clark <rob@ti.com>
5 * Andy Gross <andy.gross@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation version 2.
10 *
11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12 * kind, whether express or implied; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16#ifndef OMAP_DMM_PRIV_H
17#define OMAP_DMM_PRIV_H
18
19#define DMM_REVISION 0x000
20#define DMM_HWINFO 0x004
21#define DMM_LISA_HWINFO 0x008
22#define DMM_DMM_SYSCONFIG 0x010
23#define DMM_LISA_LOCK 0x01C
24#define DMM_LISA_MAP__0 0x040
25#define DMM_LISA_MAP__1 0x044
26#define DMM_TILER_HWINFO 0x208
27#define DMM_TILER_OR__0 0x220
28#define DMM_TILER_OR__1 0x224
29#define DMM_PAT_HWINFO 0x408
30#define DMM_PAT_GEOMETRY 0x40C
31#define DMM_PAT_CONFIG 0x410
32#define DMM_PAT_VIEW__0 0x420
33#define DMM_PAT_VIEW__1 0x424
34#define DMM_PAT_VIEW_MAP__0 0x440
35#define DMM_PAT_VIEW_MAP_BASE 0x460
36#define DMM_PAT_IRQ_EOI 0x478
37#define DMM_PAT_IRQSTATUS_RAW 0x480
38#define DMM_PAT_IRQSTATUS 0x490
39#define DMM_PAT_IRQENABLE_SET 0x4A0
40#define DMM_PAT_IRQENABLE_CLR 0x4B0
41#define DMM_PAT_STATUS__0 0x4C0
42#define DMM_PAT_STATUS__1 0x4C4
43#define DMM_PAT_STATUS__2 0x4C8
44#define DMM_PAT_STATUS__3 0x4CC
45#define DMM_PAT_DESCR__0 0x500
46#define DMM_PAT_DESCR__1 0x510
47#define DMM_PAT_DESCR__2 0x520
48#define DMM_PAT_DESCR__3 0x530
49#define DMM_PEG_HWINFO 0x608
50#define DMM_PEG_PRIO 0x620
51#define DMM_PEG_PRIO_PAT 0x640
52
53#define DMM_IRQSTAT_DST (1<<0)
54#define DMM_IRQSTAT_LST (1<<1)
55#define DMM_IRQSTAT_ERR_INV_DSC (1<<2)
56#define DMM_IRQSTAT_ERR_INV_DATA (1<<3)
57#define DMM_IRQSTAT_ERR_UPD_AREA (1<<4)
58#define DMM_IRQSTAT_ERR_UPD_CTRL (1<<5)
59#define DMM_IRQSTAT_ERR_UPD_DATA (1<<6)
60#define DMM_IRQSTAT_ERR_LUT_MISS (1<<7)
61
62#define DMM_IRQSTAT_ERR_MASK (DMM_IRQ_STAT_ERR_INV_DSC | \
63 DMM_IRQ_STAT_ERR_INV_DATA | \
64 DMM_IRQ_STAT_ERR_UPD_AREA | \
65 DMM_IRQ_STAT_ERR_UPD_CTRL | \
66 DMM_IRQ_STAT_ERR_UPD_DATA | \
67 DMM_IRQ_STAT_ERR_LUT_MISS)
68
69#define DMM_PATSTATUS_READY (1<<0)
70#define DMM_PATSTATUS_VALID (1<<1)
71#define DMM_PATSTATUS_RUN (1<<2)
72#define DMM_PATSTATUS_DONE (1<<3)
73#define DMM_PATSTATUS_LINKED (1<<4)
74#define DMM_PATSTATUS_BYPASSED (1<<7)
75#define DMM_PATSTATUS_ERR_INV_DESCR (1<<10)
76#define DMM_PATSTATUS_ERR_INV_DATA (1<<11)
77#define DMM_PATSTATUS_ERR_UPD_AREA (1<<12)
78#define DMM_PATSTATUS_ERR_UPD_CTRL (1<<13)
79#define DMM_PATSTATUS_ERR_UPD_DATA (1<<14)
80#define DMM_PATSTATUS_ERR_ACCESS (1<<15)
81
82/* note: don't treat DMM_PATSTATUS_ERR_ACCESS as an error */
83#define DMM_PATSTATUS_ERR (DMM_PATSTATUS_ERR_INV_DESCR | \
84 DMM_PATSTATUS_ERR_INV_DATA | \
85 DMM_PATSTATUS_ERR_UPD_AREA | \
86 DMM_PATSTATUS_ERR_UPD_CTRL | \
87 DMM_PATSTATUS_ERR_UPD_DATA)
88
89
90
91enum {
92 PAT_STATUS,
93 PAT_DESCR
94};
95
96struct pat_ctrl {
97 u32 start:4;
98 u32 dir:4;
99 u32 lut_id:8;
100 u32 sync:12;
101 u32 ini:4;
102};
103
104struct pat {
105 uint32_t next_pa;
106 struct pat_area area;
107 struct pat_ctrl ctrl;
108 uint32_t data_pa;
109};
110
111#define DMM_FIXED_RETRY_COUNT 1000
112
113/* create refill buffer big enough to refill all slots, plus 3 descriptors..
114 * 3 descriptors is probably the worst-case for # of 2d-slices in a 1d area,
115 * but I guess you don't hit that worst case at the same time as full area
116 * refill
117 */
118#define DESCR_SIZE 128
119#define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE))
120
121/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers.
122 * This is used in programming to address the upper portion of the LUT
123*/
124#define OMAP5_LUT_OFFSET 128
125
126struct dmm;
127
128struct dmm_txn {
129 void *engine_handle;
130 struct tcm *tcm;
131
132 uint8_t *current_va;
133 dma_addr_t current_pa;
134
135 struct pat *last_pat;
136};
137
138struct refill_engine {
139 int id;
140 struct dmm *dmm;
141 struct tcm *tcm;
142
143 uint8_t *refill_va;
144 dma_addr_t refill_pa;
145
146 /* only one trans per engine for now */
147 struct dmm_txn txn;
148
149 bool async;
150
151 wait_queue_head_t wait_for_refill;
152
153 struct list_head idle_node;
154};
155
156struct dmm {
157 struct device *dev;
158 void __iomem *base;
159 int irq;
160
161 struct page *dummy_page;
162 dma_addr_t dummy_pa;
163
164 void *refill_va;
165 dma_addr_t refill_pa;
166
167 /* refill engines */
168 wait_queue_head_t engine_queue;
169 struct list_head idle_head;
170 struct refill_engine *engines;
171 int num_engines;
172 atomic_t engine_counter;
173
174 /* container information */
175 int container_width;
176 int container_height;
177 int lut_width;
178 int lut_height;
179 int num_lut;
180
181 /* array of LUT - TCM containers */
182 struct tcm **tcm;
183
184 /* allocation list and lock */
185 struct list_head alloc_head;
186};
187
188#endif
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
new file mode 100644
index 000000000000..391021537105
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -0,0 +1,991 @@
1/*
2 * DMM IOMMU driver support functions for TI OMAP processors.
3 *
4 * Author: Rob Clark <rob@ti.com>
5 * Andy Gross <andy.gross@ti.com>
6 *
7 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation version 2.
12 *
13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
14 * kind, whether express or implied; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/platform_device.h> /* platform_device() */
21#include <linux/errno.h>
22#include <linux/sched.h>
23#include <linux/wait.h>
24#include <linux/interrupt.h>
25#include <linux/dma-mapping.h>
26#include <linux/slab.h>
27#include <linux/vmalloc.h>
28#include <linux/delay.h>
29#include <linux/mm.h>
30#include <linux/time.h>
31#include <linux/list.h>
32
33#include "omap_dmm_tiler.h"
34#include "omap_dmm_priv.h"
35
36#define DMM_DRIVER_NAME "dmm"
37
38/* mappings for associating views to luts */
39static struct tcm *containers[TILFMT_NFORMATS];
40static struct dmm *omap_dmm;
41
42/* global spinlock for protecting lists */
43static DEFINE_SPINLOCK(list_lock);
44
45/* Geometry table */
46#define GEOM(xshift, yshift, bytes_per_pixel) { \
47 .x_shft = (xshift), \
48 .y_shft = (yshift), \
49 .cpp = (bytes_per_pixel), \
50 .slot_w = 1 << (SLOT_WIDTH_BITS - (xshift)), \
51 .slot_h = 1 << (SLOT_HEIGHT_BITS - (yshift)), \
52 }
53
54static const struct {
55 uint32_t x_shft; /* unused X-bits (as part of bpp) */
56 uint32_t y_shft; /* unused Y-bits (as part of bpp) */
57 uint32_t cpp; /* bytes/chars per pixel */
58 uint32_t slot_w; /* width of each slot (in pixels) */
59 uint32_t slot_h; /* height of each slot (in pixels) */
60} geom[TILFMT_NFORMATS] = {
61 [TILFMT_8BIT] = GEOM(0, 0, 1),
62 [TILFMT_16BIT] = GEOM(0, 1, 2),
63 [TILFMT_32BIT] = GEOM(1, 1, 4),
64 [TILFMT_PAGE] = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
65};
66
67
68/* lookup table for registers w/ per-engine instances */
69static const uint32_t reg[][4] = {
70 [PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
71 DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
72 [PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
73 DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
74};
75
76/* simple allocator to grab next 16 byte aligned memory from txn */
77static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa)
78{
79 void *ptr;
80 struct refill_engine *engine = txn->engine_handle;
81
82 /* dmm programming requires 16 byte aligned addresses */
83 txn->current_pa = round_up(txn->current_pa, 16);
84 txn->current_va = (void *)round_up((long)txn->current_va, 16);
85
86 ptr = txn->current_va;
87 *pa = txn->current_pa;
88
89 txn->current_pa += sz;
90 txn->current_va += sz;
91
92 BUG_ON((txn->current_va - engine->refill_va) > REFILL_BUFFER_SIZE);
93
94 return ptr;
95}
96
97/* check status and spin until wait_mask comes true */
98static int wait_status(struct refill_engine *engine, uint32_t wait_mask)
99{
100 struct dmm *dmm = engine->dmm;
101 uint32_t r = 0, err, i;
102
103 i = DMM_FIXED_RETRY_COUNT;
104 while (true) {
105 r = readl(dmm->base + reg[PAT_STATUS][engine->id]);
106 err = r & DMM_PATSTATUS_ERR;
107 if (err)
108 return -EFAULT;
109
110 if ((r & wait_mask) == wait_mask)
111 break;
112
113 if (--i == 0)
114 return -ETIMEDOUT;
115
116 udelay(1);
117 }
118
119 return 0;
120}
121
122static void release_engine(struct refill_engine *engine)
123{
124 unsigned long flags;
125
126 spin_lock_irqsave(&list_lock, flags);
127 list_add(&engine->idle_node, &omap_dmm->idle_head);
128 spin_unlock_irqrestore(&list_lock, flags);
129
130 atomic_inc(&omap_dmm->engine_counter);
131 wake_up_interruptible(&omap_dmm->engine_queue);
132}
133
134static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
135{
136 struct dmm *dmm = arg;
137 uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS);
138 int i;
139
140 /* ack IRQ */
141 writel(status, dmm->base + DMM_PAT_IRQSTATUS);
142
143 for (i = 0; i < dmm->num_engines; i++) {
144 if (status & DMM_IRQSTAT_LST) {
145 wake_up_interruptible(&dmm->engines[i].wait_for_refill);
146
147 if (dmm->engines[i].async)
148 release_engine(&dmm->engines[i]);
149 }
150
151 status >>= 8;
152 }
153
154 return IRQ_HANDLED;
155}
156
157/**
158 * Get a handle for a DMM transaction
159 */
160static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm)
161{
162 struct dmm_txn *txn = NULL;
163 struct refill_engine *engine = NULL;
164 int ret;
165 unsigned long flags;
166
167
168 /* wait until an engine is available */
169 ret = wait_event_interruptible(omap_dmm->engine_queue,
170 atomic_add_unless(&omap_dmm->engine_counter, -1, 0));
171 if (ret)
172 return ERR_PTR(ret);
173
174 /* grab an idle engine */
175 spin_lock_irqsave(&list_lock, flags);
176 if (!list_empty(&dmm->idle_head)) {
177 engine = list_entry(dmm->idle_head.next, struct refill_engine,
178 idle_node);
179 list_del(&engine->idle_node);
180 }
181 spin_unlock_irqrestore(&list_lock, flags);
182
183 BUG_ON(!engine);
184
185 txn = &engine->txn;
186 engine->tcm = tcm;
187 txn->engine_handle = engine;
188 txn->last_pat = NULL;
189 txn->current_va = engine->refill_va;
190 txn->current_pa = engine->refill_pa;
191
192 return txn;
193}
194
195/**
196 * Add region to DMM transaction. If pages or pages[i] is NULL, then the
197 * corresponding slot is cleared (ie. dummy_pa is programmed)
198 */
199static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
200 struct page **pages, uint32_t npages, uint32_t roll)
201{
202 dma_addr_t pat_pa = 0;
203 uint32_t *data;
204 struct pat *pat;
205 struct refill_engine *engine = txn->engine_handle;
206 int columns = (1 + area->x1 - area->x0);
207 int rows = (1 + area->y1 - area->y0);
208 int i = columns*rows;
209
210 pat = alloc_dma(txn, sizeof(struct pat), &pat_pa);
211
212 if (txn->last_pat)
213 txn->last_pat->next_pa = (uint32_t)pat_pa;
214
215 pat->area = *area;
216
217 /* adjust Y coordinates based off of container parameters */
218 pat->area.y0 += engine->tcm->y_offset;
219 pat->area.y1 += engine->tcm->y_offset;
220
221 pat->ctrl = (struct pat_ctrl){
222 .start = 1,
223 .lut_id = engine->tcm->lut_id,
224 };
225
226 data = alloc_dma(txn, 4*i, &pat->data_pa);
227
228 while (i--) {
229 int n = i + roll;
230 if (n >= npages)
231 n -= npages;
232 data[i] = (pages && pages[n]) ?
233 page_to_phys(pages[n]) : engine->dmm->dummy_pa;
234 }
235
236 txn->last_pat = pat;
237
238 return;
239}
240
241/**
242 * Commit the DMM transaction.
243 */
244static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
245{
246 int ret = 0;
247 struct refill_engine *engine = txn->engine_handle;
248 struct dmm *dmm = engine->dmm;
249
250 if (!txn->last_pat) {
251 dev_err(engine->dmm->dev, "need at least one txn\n");
252 ret = -EINVAL;
253 goto cleanup;
254 }
255
256 txn->last_pat->next_pa = 0;
257
258 /* write to PAT_DESCR to clear out any pending transaction */
259 writel(0x0, dmm->base + reg[PAT_DESCR][engine->id]);
260
261 /* wait for engine ready: */
262 ret = wait_status(engine, DMM_PATSTATUS_READY);
263 if (ret) {
264 ret = -EFAULT;
265 goto cleanup;
266 }
267
268 /* mark whether it is async to denote list management in IRQ handler */
269 engine->async = wait ? false : true;
270
271 /* kick reload */
272 writel(engine->refill_pa,
273 dmm->base + reg[PAT_DESCR][engine->id]);
274
275 if (wait) {
276 if (wait_event_interruptible_timeout(engine->wait_for_refill,
277 wait_status(engine, DMM_PATSTATUS_READY) == 0,
278 msecs_to_jiffies(1)) <= 0) {
279 dev_err(dmm->dev, "timed out waiting for done\n");
280 ret = -ETIMEDOUT;
281 }
282 }
283
284cleanup:
285 /* only place engine back on list if we are done with it */
286 if (ret || wait)
287 release_engine(engine);
288
289 return ret;
290}
291
292/*
293 * DMM programming
294 */
295static int fill(struct tcm_area *area, struct page **pages,
296 uint32_t npages, uint32_t roll, bool wait)
297{
298 int ret = 0;
299 struct tcm_area slice, area_s;
300 struct dmm_txn *txn;
301
302 txn = dmm_txn_init(omap_dmm, area->tcm);
303 if (IS_ERR_OR_NULL(txn))
304 return -ENOMEM;
305
306 tcm_for_each_slice(slice, *area, area_s) {
307 struct pat_area p_area = {
308 .x0 = slice.p0.x, .y0 = slice.p0.y,
309 .x1 = slice.p1.x, .y1 = slice.p1.y,
310 };
311
312 dmm_txn_append(txn, &p_area, pages, npages, roll);
313
314 roll += tcm_sizeof(slice);
315 }
316
317 ret = dmm_txn_commit(txn, wait);
318
319 return ret;
320}
321
322/*
323 * Pin/unpin
324 */
325
326/* note: slots for which pages[i] == NULL are filled w/ dummy page
327 */
328int tiler_pin(struct tiler_block *block, struct page **pages,
329 uint32_t npages, uint32_t roll, bool wait)
330{
331 int ret;
332
333 ret = fill(&block->area, pages, npages, roll, wait);
334
335 if (ret)
336 tiler_unpin(block);
337
338 return ret;
339}
340
341int tiler_unpin(struct tiler_block *block)
342{
343 return fill(&block->area, NULL, 0, 0, false);
344}
345
346/*
347 * Reserve/release
348 */
349struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w,
350 uint16_t h, uint16_t align)
351{
352 struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
353 u32 min_align = 128;
354 int ret;
355 unsigned long flags;
356
357 BUG_ON(!validfmt(fmt));
358
359 /* convert width/height to slots */
360 w = DIV_ROUND_UP(w, geom[fmt].slot_w);
361 h = DIV_ROUND_UP(h, geom[fmt].slot_h);
362
363 /* convert alignment to slots */
364 min_align = max(min_align, (geom[fmt].slot_w * geom[fmt].cpp));
365 align = ALIGN(align, min_align);
366 align /= geom[fmt].slot_w * geom[fmt].cpp;
367
368 block->fmt = fmt;
369
370 ret = tcm_reserve_2d(containers[fmt], w, h, align, &block->area);
371 if (ret) {
372 kfree(block);
373 return ERR_PTR(-ENOMEM);
374 }
375
376 /* add to allocation list */
377 spin_lock_irqsave(&list_lock, flags);
378 list_add(&block->alloc_node, &omap_dmm->alloc_head);
379 spin_unlock_irqrestore(&list_lock, flags);
380
381 return block;
382}
383
384struct tiler_block *tiler_reserve_1d(size_t size)
385{
386 struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
387 int num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
388 unsigned long flags;
389
390 if (!block)
391 return ERR_PTR(-ENOMEM);
392
393 block->fmt = TILFMT_PAGE;
394
395 if (tcm_reserve_1d(containers[TILFMT_PAGE], num_pages,
396 &block->area)) {
397 kfree(block);
398 return ERR_PTR(-ENOMEM);
399 }
400
401 spin_lock_irqsave(&list_lock, flags);
402 list_add(&block->alloc_node, &omap_dmm->alloc_head);
403 spin_unlock_irqrestore(&list_lock, flags);
404
405 return block;
406}
407
408/* note: if you have pin'd pages, you should have already unpin'd first! */
409int tiler_release(struct tiler_block *block)
410{
411 int ret = tcm_free(&block->area);
412 unsigned long flags;
413
414 if (block->area.tcm)
415 dev_err(omap_dmm->dev, "failed to release block\n");
416
417 spin_lock_irqsave(&list_lock, flags);
418 list_del(&block->alloc_node);
419 spin_unlock_irqrestore(&list_lock, flags);
420
421 kfree(block);
422 return ret;
423}
424
425/*
426 * Utils
427 */
428
429/* calculate the tiler space address of a pixel in a view orientation...
430 * below description copied from the display subsystem section of TRM:
431 *
432 * When the TILER is addressed, the bits:
433 * [28:27] = 0x0 for 8-bit tiled
434 * 0x1 for 16-bit tiled
435 * 0x2 for 32-bit tiled
436 * 0x3 for page mode
437 * [31:29] = 0x0 for 0-degree view
438 * 0x1 for 180-degree view + mirroring
439 * 0x2 for 0-degree view + mirroring
440 * 0x3 for 180-degree view
441 * 0x4 for 270-degree view + mirroring
442 * 0x5 for 270-degree view
443 * 0x6 for 90-degree view
444 * 0x7 for 90-degree view + mirroring
445 * Otherwise the bits indicated the corresponding bit address to access
446 * the SDRAM.
447 */
448static u32 tiler_get_address(enum tiler_fmt fmt, u32 orient, u32 x, u32 y)
449{
450 u32 x_bits, y_bits, tmp, x_mask, y_mask, alignment;
451
452 x_bits = CONT_WIDTH_BITS - geom[fmt].x_shft;
453 y_bits = CONT_HEIGHT_BITS - geom[fmt].y_shft;
454 alignment = geom[fmt].x_shft + geom[fmt].y_shft;
455
456 /* validate coordinate */
457 x_mask = MASK(x_bits);
458 y_mask = MASK(y_bits);
459
460 if (x < 0 || x > x_mask || y < 0 || y > y_mask) {
461 DBG("invalid coords: %u < 0 || %u > %u || %u < 0 || %u > %u",
462 x, x, x_mask, y, y, y_mask);
463 return 0;
464 }
465
466 /* account for mirroring */
467 if (orient & MASK_X_INVERT)
468 x ^= x_mask;
469 if (orient & MASK_Y_INVERT)
470 y ^= y_mask;
471
472 /* get coordinate address */
473 if (orient & MASK_XY_FLIP)
474 tmp = ((x << y_bits) + y);
475 else
476 tmp = ((y << x_bits) + x);
477
478 return TIL_ADDR((tmp << alignment), orient, fmt);
479}
480
481dma_addr_t tiler_ssptr(struct tiler_block *block)
482{
483 BUG_ON(!validfmt(block->fmt));
484
485 return TILVIEW_8BIT + tiler_get_address(block->fmt, 0,
486 block->area.p0.x * geom[block->fmt].slot_w,
487 block->area.p0.y * geom[block->fmt].slot_h);
488}
489
490dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient,
491 uint32_t x, uint32_t y)
492{
493 struct tcm_pt *p = &block->area.p0;
494 BUG_ON(!validfmt(block->fmt));
495
496 return tiler_get_address(block->fmt, orient,
497 (p->x * geom[block->fmt].slot_w) + x,
498 (p->y * geom[block->fmt].slot_h) + y);
499}
500
501void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h)
502{
503 BUG_ON(!validfmt(fmt));
504 *w = round_up(*w, geom[fmt].slot_w);
505 *h = round_up(*h, geom[fmt].slot_h);
506}
507
508uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient)
509{
510 BUG_ON(!validfmt(fmt));
511
512 if (orient & MASK_XY_FLIP)
513 return 1 << (CONT_HEIGHT_BITS + geom[fmt].x_shft);
514 else
515 return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft);
516}
517
518size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h)
519{
520 tiler_align(fmt, &w, &h);
521 return geom[fmt].cpp * w * h;
522}
523
524size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h)
525{
526 BUG_ON(!validfmt(fmt));
527 return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h;
528}
529
530bool dmm_is_available(void)
531{
532 return omap_dmm ? true : false;
533}
534
535static int omap_dmm_remove(struct platform_device *dev)
536{
537 struct tiler_block *block, *_block;
538 int i;
539 unsigned long flags;
540
541 if (omap_dmm) {
542 /* free all area regions */
543 spin_lock_irqsave(&list_lock, flags);
544 list_for_each_entry_safe(block, _block, &omap_dmm->alloc_head,
545 alloc_node) {
546 list_del(&block->alloc_node);
547 kfree(block);
548 }
549 spin_unlock_irqrestore(&list_lock, flags);
550
551 for (i = 0; i < omap_dmm->num_lut; i++)
552 if (omap_dmm->tcm && omap_dmm->tcm[i])
553 omap_dmm->tcm[i]->deinit(omap_dmm->tcm[i]);
554 kfree(omap_dmm->tcm);
555
556 kfree(omap_dmm->engines);
557 if (omap_dmm->refill_va)
558 dma_free_writecombine(omap_dmm->dev,
559 REFILL_BUFFER_SIZE * omap_dmm->num_engines,
560 omap_dmm->refill_va,
561 omap_dmm->refill_pa);
562 if (omap_dmm->dummy_page)
563 __free_page(omap_dmm->dummy_page);
564
565 if (omap_dmm->irq > 0)
566 free_irq(omap_dmm->irq, omap_dmm);
567
568 iounmap(omap_dmm->base);
569 kfree(omap_dmm);
570 omap_dmm = NULL;
571 }
572
573 return 0;
574}
575
576static int omap_dmm_probe(struct platform_device *dev)
577{
578 int ret = -EFAULT, i;
579 struct tcm_area area = {0};
580 u32 hwinfo, pat_geom;
581 struct resource *mem;
582
583 omap_dmm = kzalloc(sizeof(*omap_dmm), GFP_KERNEL);
584 if (!omap_dmm) {
585 dev_err(&dev->dev, "failed to allocate driver data section\n");
586 goto fail;
587 }
588
589 /* initialize lists */
590 INIT_LIST_HEAD(&omap_dmm->alloc_head);
591 INIT_LIST_HEAD(&omap_dmm->idle_head);
592
593 init_waitqueue_head(&omap_dmm->engine_queue);
594
595 /* lookup hwmod data - base address and irq */
596 mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
597 if (!mem) {
598 dev_err(&dev->dev, "failed to get base address resource\n");
599 goto fail;
600 }
601
602 omap_dmm->base = ioremap(mem->start, SZ_2K);
603
604 if (!omap_dmm->base) {
605 dev_err(&dev->dev, "failed to get dmm base address\n");
606 goto fail;
607 }
608
609 omap_dmm->irq = platform_get_irq(dev, 0);
610 if (omap_dmm->irq < 0) {
611 dev_err(&dev->dev, "failed to get IRQ resource\n");
612 goto fail;
613 }
614
615 omap_dmm->dev = &dev->dev;
616
617 hwinfo = readl(omap_dmm->base + DMM_PAT_HWINFO);
618 omap_dmm->num_engines = (hwinfo >> 24) & 0x1F;
619 omap_dmm->num_lut = (hwinfo >> 16) & 0x1F;
620 omap_dmm->container_width = 256;
621 omap_dmm->container_height = 128;
622
623 atomic_set(&omap_dmm->engine_counter, omap_dmm->num_engines);
624
625 /* read out actual LUT width and height */
626 pat_geom = readl(omap_dmm->base + DMM_PAT_GEOMETRY);
627 omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5;
628 omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5;
629
630 /* increment LUT by one if on OMAP5 */
631 /* LUT has twice the height, and is split into a separate container */
632 if (omap_dmm->lut_height != omap_dmm->container_height)
633 omap_dmm->num_lut++;
634
635 /* initialize DMM registers */
636 writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0);
637 writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1);
638 writel(0x80808080, omap_dmm->base + DMM_PAT_VIEW_MAP__0);
639 writel(0x80000000, omap_dmm->base + DMM_PAT_VIEW_MAP_BASE);
640 writel(0x88888888, omap_dmm->base + DMM_TILER_OR__0);
641 writel(0x88888888, omap_dmm->base + DMM_TILER_OR__1);
642
643 ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED,
644 "omap_dmm_irq_handler", omap_dmm);
645
646 if (ret) {
647 dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n",
648 omap_dmm->irq, ret);
649 omap_dmm->irq = -1;
650 goto fail;
651 }
652
653 /* Enable all interrupts for each refill engine except
654 * ERR_LUT_MISS<n> (which is just advisory, and we don't care
655 * about because we want to be able to refill live scanout
656 * buffers for accelerated pan/scroll) and FILL_DSC<n> which
657 * we just generally don't care about.
658 */
659 writel(0x7e7e7e7e, omap_dmm->base + DMM_PAT_IRQENABLE_SET);
660
661 omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32);
662 if (!omap_dmm->dummy_page) {
663 dev_err(&dev->dev, "could not allocate dummy page\n");
664 ret = -ENOMEM;
665 goto fail;
666 }
667
668 /* set dma mask for device */
669 /* NOTE: this is a workaround for the hwmod not initializing properly */
670 dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
671
672 omap_dmm->dummy_pa = page_to_phys(omap_dmm->dummy_page);
673
674 /* alloc refill memory */
675 omap_dmm->refill_va = dma_alloc_writecombine(&dev->dev,
676 REFILL_BUFFER_SIZE * omap_dmm->num_engines,
677 &omap_dmm->refill_pa, GFP_KERNEL);
678 if (!omap_dmm->refill_va) {
679 dev_err(&dev->dev, "could not allocate refill memory\n");
680 goto fail;
681 }
682
683 /* alloc engines */
684 omap_dmm->engines = kzalloc(
685 omap_dmm->num_engines * sizeof(struct refill_engine),
686 GFP_KERNEL);
687 if (!omap_dmm->engines) {
688 dev_err(&dev->dev, "could not allocate engines\n");
689 ret = -ENOMEM;
690 goto fail;
691 }
692
693 for (i = 0; i < omap_dmm->num_engines; i++) {
694 omap_dmm->engines[i].id = i;
695 omap_dmm->engines[i].dmm = omap_dmm;
696 omap_dmm->engines[i].refill_va = omap_dmm->refill_va +
697 (REFILL_BUFFER_SIZE * i);
698 omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa +
699 (REFILL_BUFFER_SIZE * i);
700 init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill);
701
702 list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head);
703 }
704
705 omap_dmm->tcm = kzalloc(omap_dmm->num_lut * sizeof(*omap_dmm->tcm),
706 GFP_KERNEL);
707 if (!omap_dmm->tcm) {
708 dev_err(&dev->dev, "failed to allocate lut ptrs\n");
709 ret = -ENOMEM;
710 goto fail;
711 }
712
713 /* init containers */
714 /* Each LUT is associated with a TCM (container manager). We use the
715 lut_id to denote the lut_id used to identify the correct LUT for
716 programming during reill operations */
717 for (i = 0; i < omap_dmm->num_lut; i++) {
718 omap_dmm->tcm[i] = sita_init(omap_dmm->container_width,
719 omap_dmm->container_height,
720 NULL);
721
722 if (!omap_dmm->tcm[i]) {
723 dev_err(&dev->dev, "failed to allocate container\n");
724 ret = -ENOMEM;
725 goto fail;
726 }
727
728 omap_dmm->tcm[i]->lut_id = i;
729 }
730
731 /* assign access mode containers to applicable tcm container */
732 /* OMAP 4 has 1 container for all 4 views */
733 /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */
734 containers[TILFMT_8BIT] = omap_dmm->tcm[0];
735 containers[TILFMT_16BIT] = omap_dmm->tcm[0];
736 containers[TILFMT_32BIT] = omap_dmm->tcm[0];
737
738 if (omap_dmm->container_height != omap_dmm->lut_height) {
739 /* second LUT is used for PAGE mode. Programming must use
740 y offset that is added to all y coordinates. LUT id is still
741 0, because it is the same LUT, just the upper 128 lines */
742 containers[TILFMT_PAGE] = omap_dmm->tcm[1];
743 omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET;
744 omap_dmm->tcm[1]->lut_id = 0;
745 } else {
746 containers[TILFMT_PAGE] = omap_dmm->tcm[0];
747 }
748
749 area = (struct tcm_area) {
750 .tcm = NULL,
751 .p1.x = omap_dmm->container_width - 1,
752 .p1.y = omap_dmm->container_height - 1,
753 };
754
755 /* initialize all LUTs to dummy page entries */
756 for (i = 0; i < omap_dmm->num_lut; i++) {
757 area.tcm = omap_dmm->tcm[i];
758 if (fill(&area, NULL, 0, 0, true))
759 dev_err(omap_dmm->dev, "refill failed");
760 }
761
762 dev_info(omap_dmm->dev, "initialized all PAT entries\n");
763
764 return 0;
765
766fail:
767 if (omap_dmm_remove(dev))
768 dev_err(&dev->dev, "cleanup failed\n");
769 return ret;
770}
771
772/*
773 * debugfs support
774 */
775
776#ifdef CONFIG_DEBUG_FS
777
778static const char *alphabet = "abcdefghijklmnopqrstuvwxyz"
779 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
780static const char *special = ".,:;'\"`~!^-+";
781
782static void fill_map(char **map, int xdiv, int ydiv, struct tcm_area *a,
783 char c, bool ovw)
784{
785 int x, y;
786 for (y = a->p0.y / ydiv; y <= a->p1.y / ydiv; y++)
787 for (x = a->p0.x / xdiv; x <= a->p1.x / xdiv; x++)
788 if (map[y][x] == ' ' || ovw)
789 map[y][x] = c;
790}
791
792static void fill_map_pt(char **map, int xdiv, int ydiv, struct tcm_pt *p,
793 char c)
794{
795 map[p->y / ydiv][p->x / xdiv] = c;
796}
797
798static char read_map_pt(char **map, int xdiv, int ydiv, struct tcm_pt *p)
799{
800 return map[p->y / ydiv][p->x / xdiv];
801}
802
803static int map_width(int xdiv, int x0, int x1)
804{
805 return (x1 / xdiv) - (x0 / xdiv) + 1;
806}
807
808static void text_map(char **map, int xdiv, char *nice, int yd, int x0, int x1)
809{
810 char *p = map[yd] + (x0 / xdiv);
811 int w = (map_width(xdiv, x0, x1) - strlen(nice)) / 2;
812 if (w >= 0) {
813 p += w;
814 while (*nice)
815 *p++ = *nice++;
816 }
817}
818
819static void map_1d_info(char **map, int xdiv, int ydiv, char *nice,
820 struct tcm_area *a)
821{
822 sprintf(nice, "%dK", tcm_sizeof(*a) * 4);
823 if (a->p0.y + 1 < a->p1.y) {
824 text_map(map, xdiv, nice, (a->p0.y + a->p1.y) / 2 / ydiv, 0,
825 256 - 1);
826 } else if (a->p0.y < a->p1.y) {
827 if (strlen(nice) < map_width(xdiv, a->p0.x, 256 - 1))
828 text_map(map, xdiv, nice, a->p0.y / ydiv,
829 a->p0.x + xdiv, 256 - 1);
830 else if (strlen(nice) < map_width(xdiv, 0, a->p1.x))
831 text_map(map, xdiv, nice, a->p1.y / ydiv,
832 0, a->p1.y - xdiv);
833 } else if (strlen(nice) + 1 < map_width(xdiv, a->p0.x, a->p1.x)) {
834 text_map(map, xdiv, nice, a->p0.y / ydiv, a->p0.x, a->p1.x);
835 }
836}
837
838static void map_2d_info(char **map, int xdiv, int ydiv, char *nice,
839 struct tcm_area *a)
840{
841 sprintf(nice, "(%d*%d)", tcm_awidth(*a), tcm_aheight(*a));
842 if (strlen(nice) + 1 < map_width(xdiv, a->p0.x, a->p1.x))
843 text_map(map, xdiv, nice, (a->p0.y + a->p1.y) / 2 / ydiv,
844 a->p0.x, a->p1.x);
845}
846
847int tiler_map_show(struct seq_file *s, void *arg)
848{
849 int xdiv = 2, ydiv = 1;
850 char **map = NULL, *global_map;
851 struct tiler_block *block;
852 struct tcm_area a, p;
853 int i;
854 const char *m2d = alphabet;
855 const char *a2d = special;
856 const char *m2dp = m2d, *a2dp = a2d;
857 char nice[128];
858 int h_adj;
859 int w_adj;
860 unsigned long flags;
861 int lut_idx;
862
863
864 if (!omap_dmm) {
865 /* early return if dmm/tiler device is not initialized */
866 return 0;
867 }
868
869 h_adj = omap_dmm->container_height / ydiv;
870 w_adj = omap_dmm->container_width / xdiv;
871
872 map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL);
873 global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL);
874
875 if (!map || !global_map)
876 goto error;
877
878 for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) {
879 memset(map, 0, sizeof(h_adj * sizeof(*map)));
880 memset(global_map, ' ', (w_adj + 1) * h_adj);
881
882 for (i = 0; i < omap_dmm->container_height; i++) {
883 map[i] = global_map + i * (w_adj + 1);
884 map[i][w_adj] = 0;
885 }
886
887 spin_lock_irqsave(&list_lock, flags);
888
889 list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) {
890 if (block->area.tcm == omap_dmm->tcm[lut_idx]) {
891 if (block->fmt != TILFMT_PAGE) {
892 fill_map(map, xdiv, ydiv, &block->area,
893 *m2dp, true);
894 if (!*++a2dp)
895 a2dp = a2d;
896 if (!*++m2dp)
897 m2dp = m2d;
898 map_2d_info(map, xdiv, ydiv, nice,
899 &block->area);
900 } else {
901 bool start = read_map_pt(map, xdiv,
902 ydiv, &block->area.p0) == ' ';
903 bool end = read_map_pt(map, xdiv, ydiv,
904 &block->area.p1) == ' ';
905
906 tcm_for_each_slice(a, block->area, p)
907 fill_map(map, xdiv, ydiv, &a,
908 '=', true);
909 fill_map_pt(map, xdiv, ydiv,
910 &block->area.p0,
911 start ? '<' : 'X');
912 fill_map_pt(map, xdiv, ydiv,
913 &block->area.p1,
914 end ? '>' : 'X');
915 map_1d_info(map, xdiv, ydiv, nice,
916 &block->area);
917 }
918 }
919 }
920
921 spin_unlock_irqrestore(&list_lock, flags);
922
923 if (s) {
924 seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx);
925 for (i = 0; i < 128; i++)
926 seq_printf(s, "%03d:%s\n", i, map[i]);
927 seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx);
928 } else {
929 dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n",
930 lut_idx);
931 for (i = 0; i < 128; i++)
932 dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]);
933 dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n",
934 lut_idx);
935 }
936 }
937
938error:
939 kfree(map);
940 kfree(global_map);
941
942 return 0;
943}
944#endif
945
946#ifdef CONFIG_PM
947static int omap_dmm_resume(struct device *dev)
948{
949 struct tcm_area area;
950 int i;
951
952 if (!omap_dmm)
953 return -ENODEV;
954
955 area = (struct tcm_area) {
956 .tcm = NULL,
957 .p1.x = omap_dmm->container_width - 1,
958 .p1.y = omap_dmm->container_height - 1,
959 };
960
961 /* initialize all LUTs to dummy page entries */
962 for (i = 0; i < omap_dmm->num_lut; i++) {
963 area.tcm = omap_dmm->tcm[i];
964 if (fill(&area, NULL, 0, 0, true))
965 dev_err(dev, "refill failed");
966 }
967
968 return 0;
969}
970
971static const struct dev_pm_ops omap_dmm_pm_ops = {
972 .resume = omap_dmm_resume,
973};
974#endif
975
976struct platform_driver omap_dmm_driver = {
977 .probe = omap_dmm_probe,
978 .remove = omap_dmm_remove,
979 .driver = {
980 .owner = THIS_MODULE,
981 .name = DMM_DRIVER_NAME,
982#ifdef CONFIG_PM
983 .pm = &omap_dmm_pm_ops,
984#endif
985 },
986};
987
988MODULE_LICENSE("GPL v2");
989MODULE_AUTHOR("Andy Gross <andy.gross@ti.com>");
990MODULE_DESCRIPTION("OMAP DMM/Tiler Driver");
991MODULE_ALIAS("platform:" DMM_DRIVER_NAME);
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
new file mode 100644
index 000000000000..4fdd61e54bd2
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
@@ -0,0 +1,141 @@
1/*
2 *
3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Rob Clark <rob@ti.com>
5 * Andy Gross <andy.gross@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation version 2.
10 *
11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12 * kind, whether express or implied; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16#ifndef OMAP_DMM_TILER_H
17#define OMAP_DMM_TILER_H
18
19#include "omap_drv.h"
20#include "tcm.h"
21
22enum tiler_fmt {
23 TILFMT_8BIT = 0,
24 TILFMT_16BIT,
25 TILFMT_32BIT,
26 TILFMT_PAGE,
27 TILFMT_NFORMATS
28};
29
30struct pat_area {
31 u32 x0:8;
32 u32 y0:8;
33 u32 x1:8;
34 u32 y1:8;
35};
36
37struct tiler_block {
38 struct list_head alloc_node; /* node for global block list */
39 struct tcm_area area; /* area */
40 enum tiler_fmt fmt; /* format */
41};
42
43/* bits representing the same slot in DMM-TILER hw-block */
44#define SLOT_WIDTH_BITS 6
45#define SLOT_HEIGHT_BITS 6
46
47/* bits reserved to describe coordinates in DMM-TILER hw-block */
48#define CONT_WIDTH_BITS 14
49#define CONT_HEIGHT_BITS 13
50
51/* calculated constants */
52#define TILER_PAGE (1 << (SLOT_WIDTH_BITS + SLOT_HEIGHT_BITS))
53#define TILER_WIDTH (1 << (CONT_WIDTH_BITS - SLOT_WIDTH_BITS))
54#define TILER_HEIGHT (1 << (CONT_HEIGHT_BITS - SLOT_HEIGHT_BITS))
55
56/*
57Table 15-11. Coding and Description of TILER Orientations
58S Y X Description Alternate description
590 0 0 0-degree view Natural view
600 0 1 0-degree view with vertical mirror 180-degree view with horizontal mirror
610 1 0 0-degree view with horizontal mirror 180-degree view with vertical mirror
620 1 1 180-degree view
631 0 0 90-degree view with vertical mirror 270-degree view with horizontal mirror
641 0 1 270-degree view
651 1 0 90-degree view
661 1 1 90-degree view with horizontal mirror 270-degree view with vertical mirror
67 */
68#define MASK_XY_FLIP (1 << 31)
69#define MASK_Y_INVERT (1 << 30)
70#define MASK_X_INVERT (1 << 29)
71#define SHIFT_ACC_MODE 27
72#define MASK_ACC_MODE 3
73
74#define MASK(bits) ((1 << (bits)) - 1)
75
76#define TILVIEW_8BIT 0x60000000u
77#define TILVIEW_16BIT (TILVIEW_8BIT + VIEW_SIZE)
78#define TILVIEW_32BIT (TILVIEW_16BIT + VIEW_SIZE)
79#define TILVIEW_PAGE (TILVIEW_32BIT + VIEW_SIZE)
80#define TILVIEW_END (TILVIEW_PAGE + VIEW_SIZE)
81
82/* create tsptr by adding view orientation and access mode */
83#define TIL_ADDR(x, orient, a)\
84 ((u32) (x) | (orient) | ((a) << SHIFT_ACC_MODE))
85
86#ifdef CONFIG_DEBUG_FS
87int tiler_map_show(struct seq_file *s, void *arg);
88#endif
89
90/* pin/unpin */
91int tiler_pin(struct tiler_block *block, struct page **pages,
92 uint32_t npages, uint32_t roll, bool wait);
93int tiler_unpin(struct tiler_block *block);
94
95/* reserve/release */
96struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, uint16_t h,
97 uint16_t align);
98struct tiler_block *tiler_reserve_1d(size_t size);
99int tiler_release(struct tiler_block *block);
100
101/* utilities */
102dma_addr_t tiler_ssptr(struct tiler_block *block);
103dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient,
104 uint32_t x, uint32_t y);
105uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient);
106size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
107size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
108void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
109bool dmm_is_available(void);
110
111extern struct platform_driver omap_dmm_driver;
112
113/* GEM bo flags -> tiler fmt */
114static inline enum tiler_fmt gem2fmt(uint32_t flags)
115{
116 switch (flags & OMAP_BO_TILED) {
117 case OMAP_BO_TILED_8:
118 return TILFMT_8BIT;
119 case OMAP_BO_TILED_16:
120 return TILFMT_16BIT;
121 case OMAP_BO_TILED_32:
122 return TILFMT_32BIT;
123 default:
124 return TILFMT_PAGE;
125 }
126}
127
128static inline bool validfmt(enum tiler_fmt fmt)
129{
130 switch (fmt) {
131 case TILFMT_8BIT:
132 case TILFMT_16BIT:
133 case TILFMT_32BIT:
134 case TILFMT_PAGE:
135 return true;
136 default:
137 return false;
138 }
139}
140
141#endif
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
new file mode 100644
index 000000000000..9083538bd16a
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -0,0 +1,610 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_drv.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include "drm_crtc_helper.h"
23#include "drm_fb_helper.h"
24#include "omap_dmm_tiler.h"
25
26#define DRIVER_NAME MODULE_NAME
27#define DRIVER_DESC "OMAP DRM"
28#define DRIVER_DATE "20110917"
29#define DRIVER_MAJOR 1
30#define DRIVER_MINOR 0
31#define DRIVER_PATCHLEVEL 0
32
33static int num_crtc = CONFIG_DRM_OMAP_NUM_CRTCS;
34
35MODULE_PARM_DESC(num_crtc, "Number of overlays to use as CRTCs");
36module_param(num_crtc, int, 0600);
37
38/*
39 * mode config funcs
40 */
41
42/* Notes about mapping DSS and DRM entities:
43 * CRTC: overlay
44 * encoder: manager.. with some extension to allow one primary CRTC
45 * and zero or more video CRTC's to be mapped to one encoder?
46 * connector: dssdev.. manager can be attached/detached from different
47 * devices
48 */
49
50static void omap_fb_output_poll_changed(struct drm_device *dev)
51{
52 struct omap_drm_private *priv = dev->dev_private;
53 DBG("dev=%p", dev);
54 if (priv->fbdev)
55 drm_fb_helper_hotplug_event(priv->fbdev);
56}
57
58static const struct drm_mode_config_funcs omap_mode_config_funcs = {
59 .fb_create = omap_framebuffer_create,
60 .output_poll_changed = omap_fb_output_poll_changed,
61};
62
63static int get_connector_type(struct omap_dss_device *dssdev)
64{
65 switch (dssdev->type) {
66 case OMAP_DISPLAY_TYPE_HDMI:
67 return DRM_MODE_CONNECTOR_HDMIA;
68 case OMAP_DISPLAY_TYPE_DPI:
69 if (!strcmp(dssdev->name, "dvi"))
70 return DRM_MODE_CONNECTOR_DVID;
71 /* fallthrough */
72 default:
73 return DRM_MODE_CONNECTOR_Unknown;
74 }
75}
76
77static int omap_modeset_init(struct drm_device *dev)
78{
79 struct omap_drm_private *priv = dev->dev_private;
80 struct omap_dss_device *dssdev = NULL;
81 int num_ovls = dss_feat_get_num_ovls();
82 int id;
83
84 drm_mode_config_init(dev);
85
86 omap_drm_irq_install(dev);
87
88 /*
89 * Create private planes and CRTCs for the last NUM_CRTCs overlay
90 * plus manager:
91 */
92 for (id = 0; id < min(num_crtc, num_ovls); id++) {
93 struct drm_plane *plane;
94 struct drm_crtc *crtc;
95
96 plane = omap_plane_init(dev, id, true);
97 crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
98
99 BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
100 priv->crtcs[id] = crtc;
101 priv->num_crtcs++;
102
103 priv->planes[id] = plane;
104 priv->num_planes++;
105 }
106
107 /*
108 * Create normal planes for the remaining overlays:
109 */
110 for (; id < num_ovls; id++) {
111 struct drm_plane *plane = omap_plane_init(dev, id, false);
112
113 BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
114 priv->planes[priv->num_planes++] = plane;
115 }
116
117 for_each_dss_dev(dssdev) {
118 struct drm_connector *connector;
119 struct drm_encoder *encoder;
120
121 if (!dssdev->driver) {
122 dev_warn(dev->dev, "%s has no driver.. skipping it\n",
123 dssdev->name);
124 return 0;
125 }
126
127 if (!(dssdev->driver->get_timings ||
128 dssdev->driver->read_edid)) {
129 dev_warn(dev->dev, "%s driver does not support "
130 "get_timings or read_edid.. skipping it!\n",
131 dssdev->name);
132 return 0;
133 }
134
135 encoder = omap_encoder_init(dev, dssdev);
136
137 if (!encoder) {
138 dev_err(dev->dev, "could not create encoder: %s\n",
139 dssdev->name);
140 return -ENOMEM;
141 }
142
143 connector = omap_connector_init(dev,
144 get_connector_type(dssdev), dssdev, encoder);
145
146 if (!connector) {
147 dev_err(dev->dev, "could not create connector: %s\n",
148 dssdev->name);
149 return -ENOMEM;
150 }
151
152 BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
153 BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
154
155 priv->encoders[priv->num_encoders++] = encoder;
156 priv->connectors[priv->num_connectors++] = connector;
157
158 drm_mode_connector_attach_encoder(connector, encoder);
159
160 /* figure out which crtc's we can connect the encoder to: */
161 encoder->possible_crtcs = 0;
162 for (id = 0; id < priv->num_crtcs; id++) {
163 enum omap_dss_output_id supported_outputs =
164 dss_feat_get_supported_outputs(pipe2chan(id));
165 if (supported_outputs & dssdev->output->id)
166 encoder->possible_crtcs |= (1 << id);
167 }
168 }
169
170 dev->mode_config.min_width = 32;
171 dev->mode_config.min_height = 32;
172
173 /* note: eventually will need some cpu_is_omapXYZ() type stuff here
174 * to fill in these limits properly on different OMAP generations..
175 */
176 dev->mode_config.max_width = 2048;
177 dev->mode_config.max_height = 2048;
178
179 dev->mode_config.funcs = &omap_mode_config_funcs;
180
181 return 0;
182}
183
184static void omap_modeset_free(struct drm_device *dev)
185{
186 drm_mode_config_cleanup(dev);
187}
188
189/*
190 * drm ioctl funcs
191 */
192
193
194static int ioctl_get_param(struct drm_device *dev, void *data,
195 struct drm_file *file_priv)
196{
197 struct omap_drm_private *priv = dev->dev_private;
198 struct drm_omap_param *args = data;
199
200 DBG("%p: param=%llu", dev, args->param);
201
202 switch (args->param) {
203 case OMAP_PARAM_CHIPSET_ID:
204 args->value = priv->omaprev;
205 break;
206 default:
207 DBG("unknown parameter %lld", args->param);
208 return -EINVAL;
209 }
210
211 return 0;
212}
213
214static int ioctl_set_param(struct drm_device *dev, void *data,
215 struct drm_file *file_priv)
216{
217 struct drm_omap_param *args = data;
218
219 switch (args->param) {
220 default:
221 DBG("unknown parameter %lld", args->param);
222 return -EINVAL;
223 }
224
225 return 0;
226}
227
228static int ioctl_gem_new(struct drm_device *dev, void *data,
229 struct drm_file *file_priv)
230{
231 struct drm_omap_gem_new *args = data;
232 VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
233 args->size.bytes, args->flags);
234 return omap_gem_new_handle(dev, file_priv, args->size,
235 args->flags, &args->handle);
236}
237
238static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
239 struct drm_file *file_priv)
240{
241 struct drm_omap_gem_cpu_prep *args = data;
242 struct drm_gem_object *obj;
243 int ret;
244
245 VERB("%p:%p: handle=%d, op=%x", dev, file_priv, args->handle, args->op);
246
247 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
248 if (!obj)
249 return -ENOENT;
250
251 ret = omap_gem_op_sync(obj, args->op);
252
253 if (!ret)
254 ret = omap_gem_op_start(obj, args->op);
255
256 drm_gem_object_unreference_unlocked(obj);
257
258 return ret;
259}
260
261static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
262 struct drm_file *file_priv)
263{
264 struct drm_omap_gem_cpu_fini *args = data;
265 struct drm_gem_object *obj;
266 int ret;
267
268 VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
269
270 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
271 if (!obj)
272 return -ENOENT;
273
274 /* XXX flushy, flushy */
275 ret = 0;
276
277 if (!ret)
278 ret = omap_gem_op_finish(obj, args->op);
279
280 drm_gem_object_unreference_unlocked(obj);
281
282 return ret;
283}
284
285static int ioctl_gem_info(struct drm_device *dev, void *data,
286 struct drm_file *file_priv)
287{
288 struct drm_omap_gem_info *args = data;
289 struct drm_gem_object *obj;
290 int ret = 0;
291
292 VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
293
294 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
295 if (!obj)
296 return -ENOENT;
297
298 args->size = omap_gem_mmap_size(obj);
299 args->offset = omap_gem_mmap_offset(obj);
300
301 drm_gem_object_unreference_unlocked(obj);
302
303 return ret;
304}
305
306struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
307 DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH),
308 DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
309 DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH),
310 DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
311 DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
312 DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH),
313};
314
315/*
316 * drm driver funcs
317 */
318
319/**
320 * load - setup chip and create an initial config
321 * @dev: DRM device
322 * @flags: startup flags
323 *
324 * The driver load routine has to do several things:
325 * - initialize the memory manager
326 * - allocate initial config memory
327 * - setup the DRM framebuffer with the allocated memory
328 */
329static int dev_load(struct drm_device *dev, unsigned long flags)
330{
331 struct omap_drm_platform_data *pdata = dev->dev->platform_data;
332 struct omap_drm_private *priv;
333 int ret;
334
335 DBG("load: dev=%p", dev);
336
337 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
338 if (!priv) {
339 dev_err(dev->dev, "could not allocate priv\n");
340 return -ENOMEM;
341 }
342
343 priv->omaprev = pdata->omaprev;
344
345 dev->dev_private = priv;
346
347 priv->wq = alloc_ordered_workqueue("omapdrm", 0);
348
349 INIT_LIST_HEAD(&priv->obj_list);
350
351 omap_gem_init(dev);
352
353 ret = omap_modeset_init(dev);
354 if (ret) {
355 dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
356 dev->dev_private = NULL;
357 kfree(priv);
358 return ret;
359 }
360
361 ret = drm_vblank_init(dev, priv->num_crtcs);
362 if (ret)
363 dev_warn(dev->dev, "could not init vblank\n");
364
365 priv->fbdev = omap_fbdev_init(dev);
366 if (!priv->fbdev) {
367 dev_warn(dev->dev, "omap_fbdev_init failed\n");
368 /* well, limp along without an fbdev.. maybe X11 will work? */
369 }
370
371 /* store off drm_device for use in pm ops */
372 dev_set_drvdata(dev->dev, dev);
373
374 drm_kms_helper_poll_init(dev);
375
376 return 0;
377}
378
379static int dev_unload(struct drm_device *dev)
380{
381 struct omap_drm_private *priv = dev->dev_private;
382
383 DBG("unload: dev=%p", dev);
384
385 drm_kms_helper_poll_fini(dev);
386 drm_vblank_cleanup(dev);
387 omap_drm_irq_uninstall(dev);
388
389 omap_fbdev_free(dev);
390 omap_modeset_free(dev);
391 omap_gem_deinit(dev);
392
393 flush_workqueue(priv->wq);
394 destroy_workqueue(priv->wq);
395
396 kfree(dev->dev_private);
397 dev->dev_private = NULL;
398
399 dev_set_drvdata(dev->dev, NULL);
400
401 return 0;
402}
403
404static int dev_open(struct drm_device *dev, struct drm_file *file)
405{
406 file->driver_priv = NULL;
407
408 DBG("open: dev=%p, file=%p", dev, file);
409
410 return 0;
411}
412
413static int dev_firstopen(struct drm_device *dev)
414{
415 DBG("firstopen: dev=%p", dev);
416 return 0;
417}
418
419/**
420 * lastclose - clean up after all DRM clients have exited
421 * @dev: DRM device
422 *
423 * Take care of cleaning up after all DRM clients have exited. In the
424 * mode setting case, we want to restore the kernel's initial mode (just
425 * in case the last client left us in a bad state).
426 */
427static void dev_lastclose(struct drm_device *dev)
428{
429 int i;
430
431 /* we don't support vga-switcheroo.. so just make sure the fbdev
432 * mode is active
433 */
434 struct omap_drm_private *priv = dev->dev_private;
435 int ret;
436
437 DBG("lastclose: dev=%p", dev);
438
439 if (priv->rotation_prop) {
440 /* need to restore default rotation state.. not sure
441 * if there is a cleaner way to restore properties to
442 * default state? Maybe a flag that properties should
443 * automatically be restored to default state on
444 * lastclose?
445 */
446 for (i = 0; i < priv->num_crtcs; i++) {
447 drm_object_property_set_value(&priv->crtcs[i]->base,
448 priv->rotation_prop, 0);
449 }
450
451 for (i = 0; i < priv->num_planes; i++) {
452 drm_object_property_set_value(&priv->planes[i]->base,
453 priv->rotation_prop, 0);
454 }
455 }
456
457 drm_modeset_lock_all(dev);
458 ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
459 drm_modeset_unlock_all(dev);
460 if (ret)
461 DBG("failed to restore crtc mode");
462}
463
464static void dev_preclose(struct drm_device *dev, struct drm_file *file)
465{
466 DBG("preclose: dev=%p", dev);
467}
468
469static void dev_postclose(struct drm_device *dev, struct drm_file *file)
470{
471 DBG("postclose: dev=%p, file=%p", dev, file);
472}
473
474static const struct vm_operations_struct omap_gem_vm_ops = {
475 .fault = omap_gem_fault,
476 .open = drm_gem_vm_open,
477 .close = drm_gem_vm_close,
478};
479
480static const struct file_operations omapdriver_fops = {
481 .owner = THIS_MODULE,
482 .open = drm_open,
483 .unlocked_ioctl = drm_ioctl,
484 .release = drm_release,
485 .mmap = omap_gem_mmap,
486 .poll = drm_poll,
487 .fasync = drm_fasync,
488 .read = drm_read,
489 .llseek = noop_llseek,
490};
491
492static struct drm_driver omap_drm_driver = {
493 .driver_features =
494 DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
495 .load = dev_load,
496 .unload = dev_unload,
497 .open = dev_open,
498 .firstopen = dev_firstopen,
499 .lastclose = dev_lastclose,
500 .preclose = dev_preclose,
501 .postclose = dev_postclose,
502 .get_vblank_counter = drm_vblank_count,
503 .enable_vblank = omap_irq_enable_vblank,
504 .disable_vblank = omap_irq_disable_vblank,
505 .irq_preinstall = omap_irq_preinstall,
506 .irq_postinstall = omap_irq_postinstall,
507 .irq_uninstall = omap_irq_uninstall,
508 .irq_handler = omap_irq_handler,
509#ifdef CONFIG_DEBUG_FS
510 .debugfs_init = omap_debugfs_init,
511 .debugfs_cleanup = omap_debugfs_cleanup,
512#endif
513 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
514 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
515 .gem_prime_export = omap_gem_prime_export,
516 .gem_prime_import = omap_gem_prime_import,
517 .gem_init_object = omap_gem_init_object,
518 .gem_free_object = omap_gem_free_object,
519 .gem_vm_ops = &omap_gem_vm_ops,
520 .dumb_create = omap_gem_dumb_create,
521 .dumb_map_offset = omap_gem_dumb_map_offset,
522 .dumb_destroy = omap_gem_dumb_destroy,
523 .ioctls = ioctls,
524 .num_ioctls = DRM_OMAP_NUM_IOCTLS,
525 .fops = &omapdriver_fops,
526 .name = DRIVER_NAME,
527 .desc = DRIVER_DESC,
528 .date = DRIVER_DATE,
529 .major = DRIVER_MAJOR,
530 .minor = DRIVER_MINOR,
531 .patchlevel = DRIVER_PATCHLEVEL,
532};
533
534static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
535{
536 DBG("");
537 return 0;
538}
539
540static int pdev_resume(struct platform_device *device)
541{
542 DBG("");
543 return 0;
544}
545
546static void pdev_shutdown(struct platform_device *device)
547{
548 DBG("");
549}
550
551static int pdev_probe(struct platform_device *device)
552{
553 DBG("%s", device->name);
554 return drm_platform_init(&omap_drm_driver, device);
555}
556
557static int pdev_remove(struct platform_device *device)
558{
559 DBG("");
560 drm_platform_exit(&omap_drm_driver, device);
561
562 platform_driver_unregister(&omap_dmm_driver);
563 return 0;
564}
565
566#ifdef CONFIG_PM
567static const struct dev_pm_ops omapdrm_pm_ops = {
568 .resume = omap_gem_resume,
569};
570#endif
571
572struct platform_driver pdev = {
573 .driver = {
574 .name = DRIVER_NAME,
575 .owner = THIS_MODULE,
576#ifdef CONFIG_PM
577 .pm = &omapdrm_pm_ops,
578#endif
579 },
580 .probe = pdev_probe,
581 .remove = pdev_remove,
582 .suspend = pdev_suspend,
583 .resume = pdev_resume,
584 .shutdown = pdev_shutdown,
585};
586
587static int __init omap_drm_init(void)
588{
589 DBG("init");
590 if (platform_driver_register(&omap_dmm_driver)) {
591 /* we can continue on without DMM.. so not fatal */
592 dev_err(NULL, "DMM registration failed\n");
593 }
594 return platform_driver_register(&pdev);
595}
596
597static void __exit omap_drm_fini(void)
598{
599 DBG("fini");
600 platform_driver_unregister(&pdev);
601}
602
603/* need late_initcall() so we load after dss_driver's are loaded */
604late_initcall(omap_drm_init);
605module_exit(omap_drm_fini);
606
607MODULE_AUTHOR("Rob Clark <rob@ti.com>");
608MODULE_DESCRIPTION("OMAP DRM Display Driver");
609MODULE_ALIAS("platform:" DRIVER_NAME);
610MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
new file mode 100644
index 000000000000..d4f997bb4ac0
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -0,0 +1,333 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_drv.h
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef __OMAP_DRV_H__
21#define __OMAP_DRV_H__
22
23#include <video/omapdss.h>
24#include <linux/module.h>
25#include <linux/types.h>
26#include <drm/drmP.h>
27#include <drm/drm_crtc_helper.h>
28#include <drm/omap_drm.h>
29#include <linux/platform_data/omap_drm.h>
30
31
32#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
33#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
34
35#define MODULE_NAME "omapdrm"
36
37/* max # of mapper-id's that can be assigned.. todo, come up with a better
38 * (but still inexpensive) way to store/access per-buffer mapper private
39 * data..
40 */
41#define MAX_MAPPERS 2
42
43/* parameters which describe (unrotated) coordinates of scanout within a fb: */
44struct omap_drm_window {
45 uint32_t rotation;
46 int32_t crtc_x, crtc_y; /* signed because can be offscreen */
47 uint32_t crtc_w, crtc_h;
48 uint32_t src_x, src_y;
49 uint32_t src_w, src_h;
50};
51
52/* Once GO bit is set, we can't make further updates to shadowed registers
53 * until the GO bit is cleared. So various parts in the kms code that need
54 * to update shadowed registers queue up a pair of callbacks, pre_apply
55 * which is called before setting GO bit, and post_apply that is called
56 * after GO bit is cleared. The crtc manages the queuing, and everyone
57 * else goes thru omap_crtc_apply() using these callbacks so that the
58 * code which has to deal w/ GO bit state is centralized.
59 */
60struct omap_drm_apply {
61 struct list_head pending_node, queued_node;
62 bool queued;
63 void (*pre_apply)(struct omap_drm_apply *apply);
64 void (*post_apply)(struct omap_drm_apply *apply);
65};
66
67/* For transiently registering for different DSS irqs that various parts
68 * of the KMS code need during setup/configuration. We these are not
69 * necessarily the same as what drm_vblank_get/put() are requesting, and
70 * the hysteresis in drm_vblank_put() is not necessarily desirable for
71 * internal housekeeping related irq usage.
72 */
73struct omap_drm_irq {
74 struct list_head node;
75 uint32_t irqmask;
76 bool registered;
77 void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus);
78};
79
80/* For KMS code that needs to wait for a certain # of IRQs:
81 */
82struct omap_irq_wait;
83struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
84 uint32_t irqmask, int count);
85int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
86 unsigned long timeout);
87
88struct omap_drm_private {
89 uint32_t omaprev;
90
91 unsigned int num_crtcs;
92 struct drm_crtc *crtcs[8];
93
94 unsigned int num_planes;
95 struct drm_plane *planes[8];
96
97 unsigned int num_encoders;
98 struct drm_encoder *encoders[8];
99
100 unsigned int num_connectors;
101 struct drm_connector *connectors[8];
102
103 struct drm_fb_helper *fbdev;
104
105 struct workqueue_struct *wq;
106
107 /* list of GEM objects: */
108 struct list_head obj_list;
109
110 bool has_dmm;
111
112 /* properties: */
113 struct drm_property *rotation_prop;
114 struct drm_property *zorder_prop;
115
116 /* irq handling: */
117 struct list_head irq_list; /* list of omap_drm_irq */
118 uint32_t vblank_mask; /* irq bits set for userspace vblank */
119 struct omap_drm_irq error_handler;
120};
121
122/* this should probably be in drm-core to standardize amongst drivers */
123#define DRM_ROTATE_0 0
124#define DRM_ROTATE_90 1
125#define DRM_ROTATE_180 2
126#define DRM_ROTATE_270 3
127#define DRM_REFLECT_X 4
128#define DRM_REFLECT_Y 5
129
130#ifdef CONFIG_DEBUG_FS
131int omap_debugfs_init(struct drm_minor *minor);
132void omap_debugfs_cleanup(struct drm_minor *minor);
133void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
134void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
135void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
136#endif
137
138#ifdef CONFIG_PM
139int omap_gem_resume(struct device *dev);
140#endif
141
142int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
143void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
144irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
145void omap_irq_preinstall(struct drm_device *dev);
146int omap_irq_postinstall(struct drm_device *dev);
147void omap_irq_uninstall(struct drm_device *dev);
148void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq);
149void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq);
150int omap_drm_irq_uninstall(struct drm_device *dev);
151int omap_drm_irq_install(struct drm_device *dev);
152
153struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev);
154void omap_fbdev_free(struct drm_device *dev);
155
156const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc);
157enum omap_channel omap_crtc_channel(struct drm_crtc *crtc);
158int omap_crtc_apply(struct drm_crtc *crtc,
159 struct omap_drm_apply *apply);
160struct drm_crtc *omap_crtc_init(struct drm_device *dev,
161 struct drm_plane *plane, enum omap_channel channel, int id);
162
163struct drm_plane *omap_plane_init(struct drm_device *dev,
164 int plane_id, bool private_plane);
165int omap_plane_dpms(struct drm_plane *plane, int mode);
166int omap_plane_mode_set(struct drm_plane *plane,
167 struct drm_crtc *crtc, struct drm_framebuffer *fb,
168 int crtc_x, int crtc_y,
169 unsigned int crtc_w, unsigned int crtc_h,
170 uint32_t src_x, uint32_t src_y,
171 uint32_t src_w, uint32_t src_h,
172 void (*fxn)(void *), void *arg);
173void omap_plane_install_properties(struct drm_plane *plane,
174 struct drm_mode_object *obj);
175int omap_plane_set_property(struct drm_plane *plane,
176 struct drm_property *property, uint64_t val);
177
178struct drm_encoder *omap_encoder_init(struct drm_device *dev,
179 struct omap_dss_device *dssdev);
180int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled);
181int omap_encoder_update(struct drm_encoder *encoder,
182 struct omap_overlay_manager *mgr,
183 struct omap_video_timings *timings);
184
185struct drm_connector *omap_connector_init(struct drm_device *dev,
186 int connector_type, struct omap_dss_device *dssdev,
187 struct drm_encoder *encoder);
188struct drm_encoder *omap_connector_attached_encoder(
189 struct drm_connector *connector);
190void omap_connector_flush(struct drm_connector *connector,
191 int x, int y, int w, int h);
192
193void copy_timings_omap_to_drm(struct drm_display_mode *mode,
194 struct omap_video_timings *timings);
195void copy_timings_drm_to_omap(struct omap_video_timings *timings,
196 struct drm_display_mode *mode);
197
198uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
199 uint32_t max_formats, enum omap_color_mode supported_modes);
200struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
201 struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd);
202struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
203 struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
204struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p);
205int omap_framebuffer_replace(struct drm_framebuffer *a,
206 struct drm_framebuffer *b, void *arg,
207 void (*unpin)(void *arg, struct drm_gem_object *bo));
208void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
209 struct omap_drm_window *win, struct omap_overlay_info *info);
210struct drm_connector *omap_framebuffer_get_next_connector(
211 struct drm_framebuffer *fb, struct drm_connector *from);
212void omap_framebuffer_flush(struct drm_framebuffer *fb,
213 int x, int y, int w, int h);
214
215void omap_gem_init(struct drm_device *dev);
216void omap_gem_deinit(struct drm_device *dev);
217
218struct drm_gem_object *omap_gem_new(struct drm_device *dev,
219 union omap_gem_size gsize, uint32_t flags);
220int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
221 union omap_gem_size gsize, uint32_t flags, uint32_t *handle);
222void omap_gem_free_object(struct drm_gem_object *obj);
223int omap_gem_init_object(struct drm_gem_object *obj);
224void *omap_gem_vaddr(struct drm_gem_object *obj);
225int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
226 uint32_t handle, uint64_t *offset);
227int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
228 uint32_t handle);
229int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
230 struct drm_mode_create_dumb *args);
231int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma);
232int omap_gem_mmap_obj(struct drm_gem_object *obj,
233 struct vm_area_struct *vma);
234int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
235int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op);
236int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op);
237int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op);
238int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
239 void (*fxn)(void *arg), void *arg);
240int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll);
241void omap_gem_cpu_sync(struct drm_gem_object *obj, int pgoff);
242void omap_gem_dma_sync(struct drm_gem_object *obj,
243 enum dma_data_direction dir);
244int omap_gem_get_paddr(struct drm_gem_object *obj,
245 dma_addr_t *paddr, bool remap);
246int omap_gem_put_paddr(struct drm_gem_object *obj);
247int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages,
248 bool remap);
249int omap_gem_put_pages(struct drm_gem_object *obj);
250uint32_t omap_gem_flags(struct drm_gem_object *obj);
251int omap_gem_rotated_paddr(struct drm_gem_object *obj, uint32_t orient,
252 int x, int y, dma_addr_t *paddr);
253uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj);
254size_t omap_gem_mmap_size(struct drm_gem_object *obj);
255int omap_gem_tiled_size(struct drm_gem_object *obj, uint16_t *w, uint16_t *h);
256int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient);
257
258struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
259 struct drm_gem_object *obj, int flags);
260struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
261 struct dma_buf *buffer);
262
263static inline int align_pitch(int pitch, int width, int bpp)
264{
265 int bytespp = (bpp + 7) / 8;
266 /* in case someone tries to feed us a completely bogus stride: */
267 pitch = max(pitch, width * bytespp);
268 /* PVR needs alignment to 8 pixels.. right now that is the most
269 * restrictive stride requirement..
270 */
271 return ALIGN(pitch, 8 * bytespp);
272}
273
274static inline enum omap_channel pipe2chan(int pipe)
275{
276 int num_mgrs = dss_feat_get_num_mgrs();
277
278 /*
279 * We usually don't want to create a CRTC for each manager,
280 * at least not until we have a way to expose private planes
281 * to userspace. Otherwise there would not be enough video
282 * pipes left for drm planes. The higher #'d managers tend
283 * to have more features so start in reverse order.
284 */
285 return num_mgrs - pipe - 1;
286}
287
288/* map crtc to vblank mask */
289static inline uint32_t pipe2vbl(int crtc)
290{
291 enum omap_channel channel = pipe2chan(crtc);
292 return dispc_mgr_get_vsync_irq(channel);
293}
294
295static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc)
296{
297 struct omap_drm_private *priv = dev->dev_private;
298 int i;
299
300 for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++)
301 if (priv->crtcs[i] == crtc)
302 return i;
303
304 BUG(); /* bogus CRTC ptr */
305 return -1;
306}
307
308/* should these be made into common util helpers?
309 */
310
311static inline int objects_lookup(struct drm_device *dev,
312 struct drm_file *filp, uint32_t pixel_format,
313 struct drm_gem_object **bos, uint32_t *handles)
314{
315 int i, n = drm_format_num_planes(pixel_format);
316
317 for (i = 0; i < n; i++) {
318 bos[i] = drm_gem_object_lookup(dev, filp, handles[i]);
319 if (!bos[i])
320 goto fail;
321
322 }
323
324 return 0;
325
326fail:
327 while (--i > 0)
328 drm_gem_object_unreference_unlocked(bos[i]);
329
330 return -ENOENT;
331}
332
333#endif /* __OMAP_DRV_H__ */
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c
new file mode 100644
index 000000000000..7e1f2ab65372
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -0,0 +1,170 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_encoder.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include "drm_crtc.h"
23#include "drm_crtc_helper.h"
24
25#include <linux/list.h>
26
27
28/*
29 * encoder funcs
30 */
31
32#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
33
34/* The encoder and connector both map to same dssdev.. the encoder
35 * handles the 'active' parts, ie. anything the modifies the state
36 * of the hw, and the connector handles the 'read-only' parts, like
37 * detecting connection and reading edid.
38 */
39struct omap_encoder {
40 struct drm_encoder base;
41 struct omap_dss_device *dssdev;
42};
43
44static void omap_encoder_destroy(struct drm_encoder *encoder)
45{
46 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
47 drm_encoder_cleanup(encoder);
48 kfree(omap_encoder);
49}
50
51static const struct drm_encoder_funcs omap_encoder_funcs = {
52 .destroy = omap_encoder_destroy,
53};
54
55/*
56 * The CRTC drm_crtc_helper_set_mode() doesn't really give us the right
57 * order.. the easiest way to work around this for now is to make all
58 * the encoder-helper's no-op's and have the omap_crtc code take care
59 * of the sequencing and call us in the right points.
60 *
61 * Eventually to handle connecting CRTCs to different encoders properly,
62 * either the CRTC helpers need to change or we need to replace
63 * drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for
64 * that.
65 */
66
67static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
68{
69}
70
71static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
72 const struct drm_display_mode *mode,
73 struct drm_display_mode *adjusted_mode)
74{
75 return true;
76}
77
78static void omap_encoder_mode_set(struct drm_encoder *encoder,
79 struct drm_display_mode *mode,
80 struct drm_display_mode *adjusted_mode)
81{
82}
83
84static void omap_encoder_prepare(struct drm_encoder *encoder)
85{
86}
87
88static void omap_encoder_commit(struct drm_encoder *encoder)
89{
90}
91
92static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
93 .dpms = omap_encoder_dpms,
94 .mode_fixup = omap_encoder_mode_fixup,
95 .mode_set = omap_encoder_mode_set,
96 .prepare = omap_encoder_prepare,
97 .commit = omap_encoder_commit,
98};
99
100/*
101 * Instead of relying on the helpers for modeset, the omap_crtc code
102 * calls these functions in the proper sequence.
103 */
104
105int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled)
106{
107 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
108 struct omap_dss_device *dssdev = omap_encoder->dssdev;
109 struct omap_dss_driver *dssdrv = dssdev->driver;
110
111 if (enabled) {
112 return dssdrv->enable(dssdev);
113 } else {
114 dssdrv->disable(dssdev);
115 return 0;
116 }
117}
118
119int omap_encoder_update(struct drm_encoder *encoder,
120 struct omap_overlay_manager *mgr,
121 struct omap_video_timings *timings)
122{
123 struct drm_device *dev = encoder->dev;
124 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
125 struct omap_dss_device *dssdev = omap_encoder->dssdev;
126 struct omap_dss_driver *dssdrv = dssdev->driver;
127 int ret;
128
129 dssdev->output->manager = mgr;
130
131 ret = dssdrv->check_timings(dssdev, timings);
132 if (ret) {
133 dev_err(dev->dev, "could not set timings: %d\n", ret);
134 return ret;
135 }
136
137 dssdrv->set_timings(dssdev, timings);
138
139 return 0;
140}
141
142/* initialize encoder */
143struct drm_encoder *omap_encoder_init(struct drm_device *dev,
144 struct omap_dss_device *dssdev)
145{
146 struct drm_encoder *encoder = NULL;
147 struct omap_encoder *omap_encoder;
148
149 omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
150 if (!omap_encoder) {
151 dev_err(dev->dev, "could not allocate encoder\n");
152 goto fail;
153 }
154
155 omap_encoder->dssdev = dssdev;
156
157 encoder = &omap_encoder->base;
158
159 drm_encoder_init(dev, encoder, &omap_encoder_funcs,
160 DRM_MODE_ENCODER_TMDS);
161 drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
162
163 return encoder;
164
165fail:
166 if (encoder)
167 omap_encoder_destroy(encoder);
168
169 return NULL;
170}
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
new file mode 100644
index 000000000000..9d5f6f696c72
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -0,0 +1,472 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_fb.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21#include "omap_dmm_tiler.h"
22
23#include "drm_crtc.h"
24#include "drm_crtc_helper.h"
25
26/*
27 * framebuffer funcs
28 */
29
30/* per-format info: */
31struct format {
32 enum omap_color_mode dss_format;
33 uint32_t pixel_format;
34 struct {
35 int stride_bpp; /* this times width is stride */
36 int sub_y; /* sub-sample in y dimension */
37 } planes[4];
38 bool yuv;
39};
40
41static const struct format formats[] = {
42 /* 16bpp [A]RGB: */
43 { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */
44 { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */
45 { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */
46 { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */
47 { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */
48 { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */
49 { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */
50 /* 24bpp RGB: */
51 { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */
52 /* 32bpp [A]RGB: */
53 { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */
54 { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */
55 { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */
56 { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */
57 /* YUV: */
58 { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true },
59 { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true },
60 { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true },
61};
62
63/* convert from overlay's pixel formats bitmask to an array of fourcc's */
64uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
65 uint32_t max_formats, enum omap_color_mode supported_modes)
66{
67 uint32_t nformats = 0;
68 int i = 0;
69
70 for (i = 0; i < ARRAY_SIZE(formats) && nformats < max_formats; i++)
71 if (formats[i].dss_format & supported_modes)
72 pixel_formats[nformats++] = formats[i].pixel_format;
73
74 return nformats;
75}
76
77/* per-plane info for the fb: */
78struct plane {
79 struct drm_gem_object *bo;
80 uint32_t pitch;
81 uint32_t offset;
82 dma_addr_t paddr;
83};
84
85#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
86
87struct omap_framebuffer {
88 struct drm_framebuffer base;
89 const struct format *format;
90 struct plane planes[4];
91};
92
93static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
94 struct drm_file *file_priv,
95 unsigned int *handle)
96{
97 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
98 return drm_gem_handle_create(file_priv,
99 omap_fb->planes[0].bo, handle);
100}
101
102static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
103{
104 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
105 int i, n = drm_format_num_planes(fb->pixel_format);
106
107 DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
108
109 drm_framebuffer_cleanup(fb);
110
111 for (i = 0; i < n; i++) {
112 struct plane *plane = &omap_fb->planes[i];
113 if (plane->bo)
114 drm_gem_object_unreference_unlocked(plane->bo);
115 }
116
117 kfree(omap_fb);
118}
119
120static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
121 struct drm_file *file_priv, unsigned flags, unsigned color,
122 struct drm_clip_rect *clips, unsigned num_clips)
123{
124 int i;
125
126 for (i = 0; i < num_clips; i++) {
127 omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
128 clips[i].x2 - clips[i].x1,
129 clips[i].y2 - clips[i].y1);
130 }
131
132 return 0;
133}
134
135static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
136 .create_handle = omap_framebuffer_create_handle,
137 .destroy = omap_framebuffer_destroy,
138 .dirty = omap_framebuffer_dirty,
139};
140
141static uint32_t get_linear_addr(struct plane *plane,
142 const struct format *format, int n, int x, int y)
143{
144 uint32_t offset;
145
146 offset = plane->offset +
147 (x * format->planes[n].stride_bpp) +
148 (y * plane->pitch / format->planes[n].sub_y);
149
150 return plane->paddr + offset;
151}
152
153/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
154 */
155void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
156 struct omap_drm_window *win, struct omap_overlay_info *info)
157{
158 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
159 const struct format *format = omap_fb->format;
160 struct plane *plane = &omap_fb->planes[0];
161 uint32_t x, y, orient = 0;
162
163 info->color_mode = format->dss_format;
164
165 info->pos_x = win->crtc_x;
166 info->pos_y = win->crtc_y;
167 info->out_width = win->crtc_w;
168 info->out_height = win->crtc_h;
169 info->width = win->src_w;
170 info->height = win->src_h;
171
172 x = win->src_x;
173 y = win->src_y;
174
175 if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) {
176 uint32_t w = win->src_w;
177 uint32_t h = win->src_h;
178
179 switch (win->rotation & 0xf) {
180 default:
181 dev_err(fb->dev->dev, "invalid rotation: %02x",
182 (uint32_t)win->rotation);
183 /* fallthru to default to no rotation */
184 case 0:
185 case BIT(DRM_ROTATE_0):
186 orient = 0;
187 break;
188 case BIT(DRM_ROTATE_90):
189 orient = MASK_XY_FLIP | MASK_X_INVERT;
190 break;
191 case BIT(DRM_ROTATE_180):
192 orient = MASK_X_INVERT | MASK_Y_INVERT;
193 break;
194 case BIT(DRM_ROTATE_270):
195 orient = MASK_XY_FLIP | MASK_Y_INVERT;
196 break;
197 }
198
199 if (win->rotation & BIT(DRM_REFLECT_X))
200 orient ^= MASK_X_INVERT;
201
202 if (win->rotation & BIT(DRM_REFLECT_Y))
203 orient ^= MASK_Y_INVERT;
204
205 /* adjust x,y offset for flip/invert: */
206 if (orient & MASK_XY_FLIP)
207 swap(w, h);
208 if (orient & MASK_Y_INVERT)
209 y += h - 1;
210 if (orient & MASK_X_INVERT)
211 x += w - 1;
212
213 omap_gem_rotated_paddr(plane->bo, orient, x, y, &info->paddr);
214 info->rotation_type = OMAP_DSS_ROT_TILER;
215 info->screen_width = omap_gem_tiled_stride(plane->bo, orient);
216 } else {
217 info->paddr = get_linear_addr(plane, format, 0, x, y);
218 info->rotation_type = OMAP_DSS_ROT_DMA;
219 info->screen_width = plane->pitch;
220 }
221
222 /* convert to pixels: */
223 info->screen_width /= format->planes[0].stride_bpp;
224
225 if (format->dss_format == OMAP_DSS_COLOR_NV12) {
226 plane = &omap_fb->planes[1];
227
228 if (info->rotation_type == OMAP_DSS_ROT_TILER) {
229 WARN_ON(!(omap_gem_flags(plane->bo) & OMAP_BO_TILED));
230 omap_gem_rotated_paddr(plane->bo, orient,
231 x/2, y/2, &info->p_uv_addr);
232 } else {
233 info->p_uv_addr = get_linear_addr(plane, format, 1, x, y);
234 }
235 } else {
236 info->p_uv_addr = 0;
237 }
238}
239
240/* Call for unpin 'a' (if not NULL), and pin 'b' (if not NULL). Although
241 * buffers to unpin are just pushed to the unpin fifo so that the
242 * caller can defer unpin until vblank.
243 *
244 * Note if this fails (ie. something went very wrong!), all buffers are
245 * unpinned, and the caller disables the overlay. We could have tried
246 * to revert back to the previous set of pinned buffers but if things are
247 * hosed there is no guarantee that would succeed.
248 */
249int omap_framebuffer_replace(struct drm_framebuffer *a,
250 struct drm_framebuffer *b, void *arg,
251 void (*unpin)(void *arg, struct drm_gem_object *bo))
252{
253 int ret = 0, i, na, nb;
254 struct omap_framebuffer *ofba = to_omap_framebuffer(a);
255 struct omap_framebuffer *ofbb = to_omap_framebuffer(b);
256 uint32_t pinned_mask = 0;
257
258 na = a ? drm_format_num_planes(a->pixel_format) : 0;
259 nb = b ? drm_format_num_planes(b->pixel_format) : 0;
260
261 for (i = 0; i < max(na, nb); i++) {
262 struct plane *pa, *pb;
263
264 pa = (i < na) ? &ofba->planes[i] : NULL;
265 pb = (i < nb) ? &ofbb->planes[i] : NULL;
266
267 if (pa)
268 unpin(arg, pa->bo);
269
270 if (pb && !ret) {
271 ret = omap_gem_get_paddr(pb->bo, &pb->paddr, true);
272 if (!ret) {
273 omap_gem_dma_sync(pb->bo, DMA_TO_DEVICE);
274 pinned_mask |= (1 << i);
275 }
276 }
277 }
278
279 if (ret) {
280 /* something went wrong.. unpin what has been pinned */
281 for (i = 0; i < nb; i++) {
282 if (pinned_mask & (1 << i)) {
283 struct plane *pb = &ofba->planes[i];
284 unpin(arg, pb->bo);
285 }
286 }
287 }
288
289 return ret;
290}
291
292struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p)
293{
294 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
295 if (p >= drm_format_num_planes(fb->pixel_format))
296 return NULL;
297 return omap_fb->planes[p].bo;
298}
299
300/* iterate thru all the connectors, returning ones that are attached
301 * to the same fb..
302 */
303struct drm_connector *omap_framebuffer_get_next_connector(
304 struct drm_framebuffer *fb, struct drm_connector *from)
305{
306 struct drm_device *dev = fb->dev;
307 struct list_head *connector_list = &dev->mode_config.connector_list;
308 struct drm_connector *connector = from;
309
310 if (!from)
311 return list_first_entry(connector_list, typeof(*from), head);
312
313 list_for_each_entry_from(connector, connector_list, head) {
314 if (connector != from) {
315 struct drm_encoder *encoder = connector->encoder;
316 struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
317 if (crtc && crtc->fb == fb)
318 return connector;
319
320 }
321 }
322
323 return NULL;
324}
325
326/* flush an area of the framebuffer (in case of manual update display that
327 * is not automatically flushed)
328 */
329void omap_framebuffer_flush(struct drm_framebuffer *fb,
330 int x, int y, int w, int h)
331{
332 struct drm_connector *connector = NULL;
333
334 VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
335
336 while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
337 /* only consider connectors that are part of a chain */
338 if (connector->encoder && connector->encoder->crtc) {
339 /* TODO: maybe this should propagate thru the crtc who
340 * could do the coordinate translation..
341 */
342 struct drm_crtc *crtc = connector->encoder->crtc;
343 int cx = max(0, x - crtc->x);
344 int cy = max(0, y - crtc->y);
345 int cw = w + (x - crtc->x) - cx;
346 int ch = h + (y - crtc->y) - cy;
347
348 omap_connector_flush(connector, cx, cy, cw, ch);
349 }
350 }
351}
352
353#ifdef CONFIG_DEBUG_FS
354void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
355{
356 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
357 int i, n = drm_format_num_planes(fb->pixel_format);
358
359 seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height,
360 (char *)&fb->pixel_format);
361
362 for (i = 0; i < n; i++) {
363 struct plane *plane = &omap_fb->planes[i];
364 seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
365 i, plane->offset, plane->pitch);
366 omap_gem_describe(plane->bo, m);
367 }
368}
369#endif
370
371struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
372 struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd)
373{
374 struct drm_gem_object *bos[4];
375 struct drm_framebuffer *fb;
376 int ret;
377
378 ret = objects_lookup(dev, file, mode_cmd->pixel_format,
379 bos, mode_cmd->handles);
380 if (ret)
381 return ERR_PTR(ret);
382
383 fb = omap_framebuffer_init(dev, mode_cmd, bos);
384 if (IS_ERR(fb)) {
385 int i, n = drm_format_num_planes(mode_cmd->pixel_format);
386 for (i = 0; i < n; i++)
387 drm_gem_object_unreference_unlocked(bos[i]);
388 return fb;
389 }
390 return fb;
391}
392
393struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
394 struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
395{
396 struct omap_framebuffer *omap_fb;
397 struct drm_framebuffer *fb = NULL;
398 const struct format *format = NULL;
399 int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
400
401 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
402 dev, mode_cmd, mode_cmd->width, mode_cmd->height,
403 (char *)&mode_cmd->pixel_format);
404
405 for (i = 0; i < ARRAY_SIZE(formats); i++) {
406 if (formats[i].pixel_format == mode_cmd->pixel_format) {
407 format = &formats[i];
408 break;
409 }
410 }
411
412 if (!format) {
413 dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
414 (char *)&mode_cmd->pixel_format);
415 ret = -EINVAL;
416 goto fail;
417 }
418
419 omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
420 if (!omap_fb) {
421 dev_err(dev->dev, "could not allocate fb\n");
422 ret = -ENOMEM;
423 goto fail;
424 }
425
426 fb = &omap_fb->base;
427 omap_fb->format = format;
428
429 for (i = 0; i < n; i++) {
430 struct plane *plane = &omap_fb->planes[i];
431 int size, pitch = mode_cmd->pitches[i];
432
433 if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) {
434 dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n",
435 pitch, mode_cmd->width * format->planes[i].stride_bpp);
436 ret = -EINVAL;
437 goto fail;
438 }
439
440 size = pitch * mode_cmd->height / format->planes[i].sub_y;
441
442 if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) {
443 dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
444 bos[i]->size - mode_cmd->offsets[i], size);
445 ret = -EINVAL;
446 goto fail;
447 }
448
449 plane->bo = bos[i];
450 plane->offset = mode_cmd->offsets[i];
451 plane->pitch = pitch;
452 plane->paddr = 0;
453 }
454
455 drm_helper_mode_fill_fb_struct(fb, mode_cmd);
456
457 ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
458 if (ret) {
459 dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
460 goto fail;
461 }
462
463 DBG("create: FB ID: %d (%p)", fb->base.id, fb);
464
465 return fb;
466
467fail:
468 if (fb)
469 omap_framebuffer_destroy(fb);
470
471 return ERR_PTR(ret);
472}
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
new file mode 100644
index 000000000000..11eed30efe06
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -0,0 +1,402 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_fbdev.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include "drm_crtc.h"
23#include "drm_fb_helper.h"
24
25MODULE_PARM_DESC(ywrap, "Enable ywrap scrolling (omap44xx and later, default 'y')");
26static bool ywrap_enabled = true;
27module_param_named(ywrap, ywrap_enabled, bool, 0644);
28
29/*
30 * fbdev funcs, to implement legacy fbdev interface on top of drm driver
31 */
32
33#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
34
35struct omap_fbdev {
36 struct drm_fb_helper base;
37 struct drm_framebuffer *fb;
38 struct drm_gem_object *bo;
39 bool ywrap_enabled;
40
41 /* for deferred dmm roll when getting called in atomic ctx */
42 struct work_struct work;
43};
44
45static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
46static struct drm_fb_helper *get_fb(struct fb_info *fbi);
47
48static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
49 size_t count, loff_t *ppos)
50{
51 ssize_t res;
52
53 res = fb_sys_write(fbi, buf, count, ppos);
54 omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
55
56 return res;
57}
58
59static void omap_fbdev_fillrect(struct fb_info *fbi,
60 const struct fb_fillrect *rect)
61{
62 sys_fillrect(fbi, rect);
63 omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
64}
65
66static void omap_fbdev_copyarea(struct fb_info *fbi,
67 const struct fb_copyarea *area)
68{
69 sys_copyarea(fbi, area);
70 omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
71}
72
73static void omap_fbdev_imageblit(struct fb_info *fbi,
74 const struct fb_image *image)
75{
76 sys_imageblit(fbi, image);
77 omap_fbdev_flush(fbi, image->dx, image->dy,
78 image->width, image->height);
79}
80
81static void pan_worker(struct work_struct *work)
82{
83 struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
84 struct fb_info *fbi = fbdev->base.fbdev;
85 int npages;
86
87 /* DMM roll shifts in 4K pages: */
88 npages = fbi->fix.line_length >> PAGE_SHIFT;
89 omap_gem_roll(fbdev->bo, fbi->var.yoffset * npages);
90}
91
92static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
93 struct fb_info *fbi)
94{
95 struct drm_fb_helper *helper = get_fb(fbi);
96 struct omap_fbdev *fbdev = to_omap_fbdev(helper);
97
98 if (!helper)
99 goto fallback;
100
101 if (!fbdev->ywrap_enabled)
102 goto fallback;
103
104 if (drm_can_sleep()) {
105 pan_worker(&fbdev->work);
106 } else {
107 struct omap_drm_private *priv = helper->dev->dev_private;
108 queue_work(priv->wq, &fbdev->work);
109 }
110
111 return 0;
112
113fallback:
114 return drm_fb_helper_pan_display(var, fbi);
115}
116
117static struct fb_ops omap_fb_ops = {
118 .owner = THIS_MODULE,
119
120 /* Note: to properly handle manual update displays, we wrap the
121 * basic fbdev ops which write to the framebuffer
122 */
123 .fb_read = fb_sys_read,
124 .fb_write = omap_fbdev_write,
125 .fb_fillrect = omap_fbdev_fillrect,
126 .fb_copyarea = omap_fbdev_copyarea,
127 .fb_imageblit = omap_fbdev_imageblit,
128
129 .fb_check_var = drm_fb_helper_check_var,
130 .fb_set_par = drm_fb_helper_set_par,
131 .fb_pan_display = omap_fbdev_pan_display,
132 .fb_blank = drm_fb_helper_blank,
133 .fb_setcmap = drm_fb_helper_setcmap,
134
135 .fb_debug_enter = drm_fb_helper_debug_enter,
136 .fb_debug_leave = drm_fb_helper_debug_leave,
137};
138
139static int omap_fbdev_create(struct drm_fb_helper *helper,
140 struct drm_fb_helper_surface_size *sizes)
141{
142 struct omap_fbdev *fbdev = to_omap_fbdev(helper);
143 struct drm_device *dev = helper->dev;
144 struct omap_drm_private *priv = dev->dev_private;
145 struct drm_framebuffer *fb = NULL;
146 union omap_gem_size gsize;
147 struct fb_info *fbi = NULL;
148 struct drm_mode_fb_cmd2 mode_cmd = {0};
149 dma_addr_t paddr;
150 int ret;
151
152 /* only doing ARGB32 since this is what is needed to alpha-blend
153 * with video overlays:
154 */
155 sizes->surface_bpp = 32;
156 sizes->surface_depth = 32;
157
158 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
159 sizes->surface_height, sizes->surface_bpp,
160 sizes->fb_width, sizes->fb_height);
161
162 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
163 sizes->surface_depth);
164
165 mode_cmd.width = sizes->surface_width;
166 mode_cmd.height = sizes->surface_height;
167
168 mode_cmd.pitches[0] = align_pitch(
169 mode_cmd.width * ((sizes->surface_bpp + 7) / 8),
170 mode_cmd.width, sizes->surface_bpp);
171
172 fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled;
173 if (fbdev->ywrap_enabled) {
174 /* need to align pitch to page size if using DMM scrolling */
175 mode_cmd.pitches[0] = ALIGN(mode_cmd.pitches[0], PAGE_SIZE);
176 }
177
178 /* allocate backing bo */
179 gsize = (union omap_gem_size){
180 .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height),
181 };
182 DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index);
183 fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC);
184 if (!fbdev->bo) {
185 dev_err(dev->dev, "failed to allocate buffer object\n");
186 ret = -ENOMEM;
187 goto fail;
188 }
189
190 fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo);
191 if (IS_ERR(fb)) {
192 dev_err(dev->dev, "failed to allocate fb\n");
193 /* note: if fb creation failed, we can't rely on fb destroy
194 * to unref the bo:
195 */
196 drm_gem_object_unreference(fbdev->bo);
197 ret = PTR_ERR(fb);
198 goto fail;
199 }
200
201 /* note: this keeps the bo pinned.. which is perhaps not ideal,
202 * but is needed as long as we use fb_mmap() to mmap to userspace
203 * (since this happens using fix.smem_start). Possibly we could
204 * implement our own mmap using GEM mmap support to avoid this
205 * (non-tiled buffer doesn't need to be pinned for fbcon to write
206 * to it). Then we just need to be sure that we are able to re-
207 * pin it in case of an opps.
208 */
209 ret = omap_gem_get_paddr(fbdev->bo, &paddr, true);
210 if (ret) {
211 dev_err(dev->dev,
212 "could not map (paddr)! Skipping framebuffer alloc\n");
213 ret = -ENOMEM;
214 goto fail;
215 }
216
217 mutex_lock(&dev->struct_mutex);
218
219 fbi = framebuffer_alloc(0, dev->dev);
220 if (!fbi) {
221 dev_err(dev->dev, "failed to allocate fb info\n");
222 ret = -ENOMEM;
223 goto fail_unlock;
224 }
225
226 DBG("fbi=%p, dev=%p", fbi, dev);
227
228 fbdev->fb = fb;
229 helper->fb = fb;
230 helper->fbdev = fbi;
231
232 fbi->par = helper;
233 fbi->flags = FBINFO_DEFAULT;
234 fbi->fbops = &omap_fb_ops;
235
236 strcpy(fbi->fix.id, MODULE_NAME);
237
238 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
239 if (ret) {
240 ret = -ENOMEM;
241 goto fail_unlock;
242 }
243
244 drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
245 drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
246
247 dev->mode_config.fb_base = paddr;
248
249 fbi->screen_base = omap_gem_vaddr(fbdev->bo);
250 fbi->screen_size = fbdev->bo->size;
251 fbi->fix.smem_start = paddr;
252 fbi->fix.smem_len = fbdev->bo->size;
253
254 /* if we have DMM, then we can use it for scrolling by just
255 * shuffling pages around in DMM rather than doing sw blit.
256 */
257 if (fbdev->ywrap_enabled) {
258 DRM_INFO("Enabling DMM ywrap scrolling\n");
259 fbi->flags |= FBINFO_HWACCEL_YWRAP | FBINFO_READS_FAST;
260 fbi->fix.ywrapstep = 1;
261 }
262
263
264 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
265 DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
266
267 mutex_unlock(&dev->struct_mutex);
268
269 return 0;
270
271fail_unlock:
272 mutex_unlock(&dev->struct_mutex);
273fail:
274
275 if (ret) {
276 if (fbi)
277 framebuffer_release(fbi);
278 if (fb) {
279 drm_framebuffer_unregister_private(fb);
280 drm_framebuffer_remove(fb);
281 }
282 }
283
284 return ret;
285}
286
287static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
288 u16 red, u16 green, u16 blue, int regno)
289{
290 DBG("fbdev: set gamma");
291}
292
293static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
294 u16 *red, u16 *green, u16 *blue, int regno)
295{
296 DBG("fbdev: get gamma");
297}
298
299static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
300 .gamma_set = omap_crtc_fb_gamma_set,
301 .gamma_get = omap_crtc_fb_gamma_get,
302 .fb_probe = omap_fbdev_create,
303};
304
305static struct drm_fb_helper *get_fb(struct fb_info *fbi)
306{
307 if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
308 /* these are not the fb's you're looking for */
309 return NULL;
310 }
311 return fbi->par;
312}
313
314/* flush an area of the framebuffer (in case of manual update display that
315 * is not automatically flushed)
316 */
317static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
318{
319 struct drm_fb_helper *helper = get_fb(fbi);
320
321 if (!helper)
322 return;
323
324 VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
325
326 omap_framebuffer_flush(helper->fb, x, y, w, h);
327}
328
329/* initialize fbdev helper */
330struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
331{
332 struct omap_drm_private *priv = dev->dev_private;
333 struct omap_fbdev *fbdev = NULL;
334 struct drm_fb_helper *helper;
335 int ret = 0;
336
337 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
338 if (!fbdev) {
339 dev_err(dev->dev, "could not allocate fbdev\n");
340 goto fail;
341 }
342
343 INIT_WORK(&fbdev->work, pan_worker);
344
345 helper = &fbdev->base;
346
347 helper->funcs = &omap_fb_helper_funcs;
348
349 ret = drm_fb_helper_init(dev, helper,
350 priv->num_crtcs, priv->num_connectors);
351 if (ret) {
352 dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
353 goto fail;
354 }
355
356 drm_fb_helper_single_add_all_connectors(helper);
357
358 /* disable all the possible outputs/crtcs before entering KMS mode */
359 drm_helper_disable_unused_functions(dev);
360
361 drm_fb_helper_initial_config(helper, 32);
362
363 priv->fbdev = helper;
364
365 return helper;
366
367fail:
368 kfree(fbdev);
369 return NULL;
370}
371
372void omap_fbdev_free(struct drm_device *dev)
373{
374 struct omap_drm_private *priv = dev->dev_private;
375 struct drm_fb_helper *helper = priv->fbdev;
376 struct omap_fbdev *fbdev;
377 struct fb_info *fbi;
378
379 DBG();
380
381 fbi = helper->fbdev;
382
383 /* only cleanup framebuffer if it is present */
384 if (fbi) {
385 unregister_framebuffer(fbi);
386 framebuffer_release(fbi);
387 }
388
389 drm_fb_helper_fini(helper);
390
391 fbdev = to_omap_fbdev(priv->fbdev);
392
393 /* this will free the backing object */
394 if (fbdev->fb) {
395 drm_framebuffer_unregister_private(fbdev->fb);
396 drm_framebuffer_remove(fbdev->fb);
397 }
398
399 kfree(fbdev);
400
401 priv->fbdev = NULL;
402}
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
new file mode 100644
index 000000000000..e8302b02691d
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -0,0 +1,1511 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_gem.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21#include <linux/spinlock.h>
22#include <linux/shmem_fs.h>
23
24#include "omap_drv.h"
25#include "omap_dmm_tiler.h"
26
27/* remove these once drm core helpers are merged */
28struct page **_drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
29void _drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages,
30 bool dirty, bool accessed);
31int _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size);
32
33/*
34 * GEM buffer object implementation.
35 */
36
37#define to_omap_bo(x) container_of(x, struct omap_gem_object, base)
38
39/* note: we use upper 8 bits of flags for driver-internal flags: */
40#define OMAP_BO_DMA 0x01000000 /* actually is physically contiguous */
41#define OMAP_BO_EXT_SYNC 0x02000000 /* externally allocated sync object */
42#define OMAP_BO_EXT_MEM 0x04000000 /* externally allocated memory */
43
44
45struct omap_gem_object {
46 struct drm_gem_object base;
47
48 struct list_head mm_list;
49
50 uint32_t flags;
51
52 /** width/height for tiled formats (rounded up to slot boundaries) */
53 uint16_t width, height;
54
55 /** roll applied when mapping to DMM */
56 uint32_t roll;
57
58 /**
59 * If buffer is allocated physically contiguous, the OMAP_BO_DMA flag
60 * is set and the paddr is valid. Also if the buffer is remapped in
61 * TILER and paddr_cnt > 0, then paddr is valid. But if you are using
62 * the physical address and OMAP_BO_DMA is not set, then you should
63 * be going thru omap_gem_{get,put}_paddr() to ensure the mapping is
64 * not removed from under your feet.
65 *
66 * Note that OMAP_BO_SCANOUT is a hint from userspace that DMA capable
67 * buffer is requested, but doesn't mean that it is. Use the
68 * OMAP_BO_DMA flag to determine if the buffer has a DMA capable
69 * physical address.
70 */
71 dma_addr_t paddr;
72
73 /**
74 * # of users of paddr
75 */
76 uint32_t paddr_cnt;
77
78 /**
79 * tiler block used when buffer is remapped in DMM/TILER.
80 */
81 struct tiler_block *block;
82
83 /**
84 * Array of backing pages, if allocated. Note that pages are never
85 * allocated for buffers originally allocated from contiguous memory
86 */
87 struct page **pages;
88
89 /** addresses corresponding to pages in above array */
90 dma_addr_t *addrs;
91
92 /**
93 * Virtual address, if mapped.
94 */
95 void *vaddr;
96
97 /**
98 * sync-object allocated on demand (if needed)
99 *
100 * Per-buffer sync-object for tracking pending and completed hw/dma
101 * read and write operations. The layout in memory is dictated by
102 * the SGX firmware, which uses this information to stall the command
103 * stream if a surface is not ready yet.
104 *
105 * Note that when buffer is used by SGX, the sync-object needs to be
106 * allocated from a special heap of sync-objects. This way many sync
107 * objects can be packed in a page, and not waste GPU virtual address
108 * space. Because of this we have to have a omap_gem_set_sync_object()
109 * API to allow replacement of the syncobj after it has (potentially)
110 * already been allocated. A bit ugly but I haven't thought of a
111 * better alternative.
112 */
113 struct {
114 uint32_t write_pending;
115 uint32_t write_complete;
116 uint32_t read_pending;
117 uint32_t read_complete;
118 } *sync;
119};
120
121static int get_pages(struct drm_gem_object *obj, struct page ***pages);
122static uint64_t mmap_offset(struct drm_gem_object *obj);
123
124/* To deal with userspace mmap'ings of 2d tiled buffers, which (a) are
125 * not necessarily pinned in TILER all the time, and (b) when they are
126 * they are not necessarily page aligned, we reserve one or more small
127 * regions in each of the 2d containers to use as a user-GART where we
128 * can create a second page-aligned mapping of parts of the buffer
129 * being accessed from userspace.
130 *
131 * Note that we could optimize slightly when we know that multiple
132 * tiler containers are backed by the same PAT.. but I'll leave that
133 * for later..
134 */
135#define NUM_USERGART_ENTRIES 2
136struct usergart_entry {
137 struct tiler_block *block; /* the reserved tiler block */
138 dma_addr_t paddr;
139 struct drm_gem_object *obj; /* the current pinned obj */
140 pgoff_t obj_pgoff; /* page offset of obj currently
141 mapped in */
142};
143static struct {
144 struct usergart_entry entry[NUM_USERGART_ENTRIES];
145 int height; /* height in rows */
146 int height_shift; /* ilog2(height in rows) */
147 int slot_shift; /* ilog2(width per slot) */
148 int stride_pfn; /* stride in pages */
149 int last; /* index of last used entry */
150} *usergart;
151
152static void evict_entry(struct drm_gem_object *obj,
153 enum tiler_fmt fmt, struct usergart_entry *entry)
154{
155 if (obj->dev->dev_mapping) {
156 struct omap_gem_object *omap_obj = to_omap_bo(obj);
157 int n = usergart[fmt].height;
158 size_t size = PAGE_SIZE * n;
159 loff_t off = mmap_offset(obj) +
160 (entry->obj_pgoff << PAGE_SHIFT);
161 const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
162 if (m > 1) {
163 int i;
164 /* if stride > than PAGE_SIZE then sparse mapping: */
165 for (i = n; i > 0; i--) {
166 unmap_mapping_range(obj->dev->dev_mapping,
167 off, PAGE_SIZE, 1);
168 off += PAGE_SIZE * m;
169 }
170 } else {
171 unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
172 }
173 }
174
175 entry->obj = NULL;
176}
177
178/* Evict a buffer from usergart, if it is mapped there */
179static void evict(struct drm_gem_object *obj)
180{
181 struct omap_gem_object *omap_obj = to_omap_bo(obj);
182
183 if (omap_obj->flags & OMAP_BO_TILED) {
184 enum tiler_fmt fmt = gem2fmt(omap_obj->flags);
185 int i;
186
187 if (!usergart)
188 return;
189
190 for (i = 0; i < NUM_USERGART_ENTRIES; i++) {
191 struct usergart_entry *entry = &usergart[fmt].entry[i];
192 if (entry->obj == obj)
193 evict_entry(obj, fmt, entry);
194 }
195 }
196}
197
198/* GEM objects can either be allocated from contiguous memory (in which
199 * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non
200 * contiguous buffers can be remapped in TILER/DMM if they need to be
201 * contiguous... but we don't do this all the time to reduce pressure
202 * on TILER/DMM space when we know at allocation time that the buffer
203 * will need to be scanned out.
204 */
205static inline bool is_shmem(struct drm_gem_object *obj)
206{
207 return obj->filp != NULL;
208}
209
210/**
211 * shmem buffers that are mapped cached can simulate coherency via using
212 * page faulting to keep track of dirty pages
213 */
214static inline bool is_cached_coherent(struct drm_gem_object *obj)
215{
216 struct omap_gem_object *omap_obj = to_omap_bo(obj);
217 return is_shmem(obj) &&
218 ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED);
219}
220
221static DEFINE_SPINLOCK(sync_lock);
222
223/** ensure backing pages are allocated */
224static int omap_gem_attach_pages(struct drm_gem_object *obj)
225{
226 struct drm_device *dev = obj->dev;
227 struct omap_gem_object *omap_obj = to_omap_bo(obj);
228 struct page **pages;
229 int npages = obj->size >> PAGE_SHIFT;
230 int i, ret;
231 dma_addr_t *addrs;
232
233 WARN_ON(omap_obj->pages);
234
235 /* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the
236 * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably
237 * we actually want CMA memory for it all anyways..
238 */
239 pages = _drm_gem_get_pages(obj, GFP_KERNEL);
240 if (IS_ERR(pages)) {
241 dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages));
242 return PTR_ERR(pages);
243 }
244
245 /* for non-cached buffers, ensure the new pages are clean because
246 * DSS, GPU, etc. are not cache coherent:
247 */
248 if (omap_obj->flags & (OMAP_BO_WC|OMAP_BO_UNCACHED)) {
249 addrs = kmalloc(npages * sizeof(*addrs), GFP_KERNEL);
250 if (!addrs) {
251 ret = -ENOMEM;
252 goto free_pages;
253 }
254
255 for (i = 0; i < npages; i++) {
256 addrs[i] = dma_map_page(dev->dev, pages[i],
257 0, PAGE_SIZE, DMA_BIDIRECTIONAL);
258 }
259 } else {
260 addrs = kzalloc(npages * sizeof(*addrs), GFP_KERNEL);
261 if (!addrs) {
262 ret = -ENOMEM;
263 goto free_pages;
264 }
265 }
266
267 omap_obj->addrs = addrs;
268 omap_obj->pages = pages;
269
270 return 0;
271
272free_pages:
273 _drm_gem_put_pages(obj, pages, true, false);
274
275 return ret;
276}
277
278/** release backing pages */
279static void omap_gem_detach_pages(struct drm_gem_object *obj)
280{
281 struct omap_gem_object *omap_obj = to_omap_bo(obj);
282
283 /* for non-cached buffers, ensure the new pages are clean because
284 * DSS, GPU, etc. are not cache coherent:
285 */
286 if (omap_obj->flags & (OMAP_BO_WC|OMAP_BO_UNCACHED)) {
287 int i, npages = obj->size >> PAGE_SHIFT;
288 for (i = 0; i < npages; i++) {
289 dma_unmap_page(obj->dev->dev, omap_obj->addrs[i],
290 PAGE_SIZE, DMA_BIDIRECTIONAL);
291 }
292 }
293
294 kfree(omap_obj->addrs);
295 omap_obj->addrs = NULL;
296
297 _drm_gem_put_pages(obj, omap_obj->pages, true, false);
298 omap_obj->pages = NULL;
299}
300
301/* get buffer flags */
302uint32_t omap_gem_flags(struct drm_gem_object *obj)
303{
304 return to_omap_bo(obj)->flags;
305}
306
307/** get mmap offset */
308static uint64_t mmap_offset(struct drm_gem_object *obj)
309{
310 struct drm_device *dev = obj->dev;
311
312 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
313
314 if (!obj->map_list.map) {
315 /* Make it mmapable */
316 size_t size = omap_gem_mmap_size(obj);
317 int ret = _drm_gem_create_mmap_offset_size(obj, size);
318
319 if (ret) {
320 dev_err(dev->dev, "could not allocate mmap offset\n");
321 return 0;
322 }
323 }
324
325 return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
326}
327
328uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
329{
330 uint64_t offset;
331 mutex_lock(&obj->dev->struct_mutex);
332 offset = mmap_offset(obj);
333 mutex_unlock(&obj->dev->struct_mutex);
334 return offset;
335}
336
337/** get mmap size */
338size_t omap_gem_mmap_size(struct drm_gem_object *obj)
339{
340 struct omap_gem_object *omap_obj = to_omap_bo(obj);
341 size_t size = obj->size;
342
343 if (omap_obj->flags & OMAP_BO_TILED) {
344 /* for tiled buffers, the virtual size has stride rounded up
345 * to 4kb.. (to hide the fact that row n+1 might start 16kb or
346 * 32kb later!). But we don't back the entire buffer with
347 * pages, only the valid picture part.. so need to adjust for
348 * this in the size used to mmap and generate mmap offset
349 */
350 size = tiler_vsize(gem2fmt(omap_obj->flags),
351 omap_obj->width, omap_obj->height);
352 }
353
354 return size;
355}
356
357/* get tiled size, returns -EINVAL if not tiled buffer */
358int omap_gem_tiled_size(struct drm_gem_object *obj, uint16_t *w, uint16_t *h)
359{
360 struct omap_gem_object *omap_obj = to_omap_bo(obj);
361 if (omap_obj->flags & OMAP_BO_TILED) {
362 *w = omap_obj->width;
363 *h = omap_obj->height;
364 return 0;
365 }
366 return -EINVAL;
367}
368
369/* Normal handling for the case of faulting in non-tiled buffers */
370static int fault_1d(struct drm_gem_object *obj,
371 struct vm_area_struct *vma, struct vm_fault *vmf)
372{
373 struct omap_gem_object *omap_obj = to_omap_bo(obj);
374 unsigned long pfn;
375 pgoff_t pgoff;
376
377 /* We don't use vmf->pgoff since that has the fake offset: */
378 pgoff = ((unsigned long)vmf->virtual_address -
379 vma->vm_start) >> PAGE_SHIFT;
380
381 if (omap_obj->pages) {
382 omap_gem_cpu_sync(obj, pgoff);
383 pfn = page_to_pfn(omap_obj->pages[pgoff]);
384 } else {
385 BUG_ON(!(omap_obj->flags & OMAP_BO_DMA));
386 pfn = (omap_obj->paddr >> PAGE_SHIFT) + pgoff;
387 }
388
389 VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
390 pfn, pfn << PAGE_SHIFT);
391
392 return vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
393}
394
395/* Special handling for the case of faulting in 2d tiled buffers */
396static int fault_2d(struct drm_gem_object *obj,
397 struct vm_area_struct *vma, struct vm_fault *vmf)
398{
399 struct omap_gem_object *omap_obj = to_omap_bo(obj);
400 struct usergart_entry *entry;
401 enum tiler_fmt fmt = gem2fmt(omap_obj->flags);
402 struct page *pages[64]; /* XXX is this too much to have on stack? */
403 unsigned long pfn;
404 pgoff_t pgoff, base_pgoff;
405 void __user *vaddr;
406 int i, ret, slots;
407
408 /*
409 * Note the height of the slot is also equal to the number of pages
410 * that need to be mapped in to fill 4kb wide CPU page. If the slot
411 * height is 64, then 64 pages fill a 4kb wide by 64 row region.
412 */
413 const int n = usergart[fmt].height;
414 const int n_shift = usergart[fmt].height_shift;
415
416 /*
417 * If buffer width in bytes > PAGE_SIZE then the virtual stride is
418 * rounded up to next multiple of PAGE_SIZE.. this need to be taken
419 * into account in some of the math, so figure out virtual stride
420 * in pages
421 */
422 const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
423
424 /* We don't use vmf->pgoff since that has the fake offset: */
425 pgoff = ((unsigned long)vmf->virtual_address -
426 vma->vm_start) >> PAGE_SHIFT;
427
428 /*
429 * Actual address we start mapping at is rounded down to previous slot
430 * boundary in the y direction:
431 */
432 base_pgoff = round_down(pgoff, m << n_shift);
433
434 /* figure out buffer width in slots */
435 slots = omap_obj->width >> usergart[fmt].slot_shift;
436
437 vaddr = vmf->virtual_address - ((pgoff - base_pgoff) << PAGE_SHIFT);
438
439 entry = &usergart[fmt].entry[usergart[fmt].last];
440
441 /* evict previous buffer using this usergart entry, if any: */
442 if (entry->obj)
443 evict_entry(entry->obj, fmt, entry);
444
445 entry->obj = obj;
446 entry->obj_pgoff = base_pgoff;
447
448 /* now convert base_pgoff to phys offset from virt offset: */
449 base_pgoff = (base_pgoff >> n_shift) * slots;
450
451 /* for wider-than 4k.. figure out which part of the slot-row we want: */
452 if (m > 1) {
453 int off = pgoff % m;
454 entry->obj_pgoff += off;
455 base_pgoff /= m;
456 slots = min(slots - (off << n_shift), n);
457 base_pgoff += off << n_shift;
458 vaddr += off << PAGE_SHIFT;
459 }
460
461 /*
462 * Map in pages. Beyond the valid pixel part of the buffer, we set
463 * pages[i] to NULL to get a dummy page mapped in.. if someone
464 * reads/writes it they will get random/undefined content, but at
465 * least it won't be corrupting whatever other random page used to
466 * be mapped in, or other undefined behavior.
467 */
468 memcpy(pages, &omap_obj->pages[base_pgoff],
469 sizeof(struct page *) * slots);
470 memset(pages + slots, 0,
471 sizeof(struct page *) * (n - slots));
472
473 ret = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true);
474 if (ret) {
475 dev_err(obj->dev->dev, "failed to pin: %d\n", ret);
476 return ret;
477 }
478
479 pfn = entry->paddr >> PAGE_SHIFT;
480
481 VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
482 pfn, pfn << PAGE_SHIFT);
483
484 for (i = n; i > 0; i--) {
485 vm_insert_mixed(vma, (unsigned long)vaddr, pfn);
486 pfn += usergart[fmt].stride_pfn;
487 vaddr += PAGE_SIZE * m;
488 }
489
490 /* simple round-robin: */
491 usergart[fmt].last = (usergart[fmt].last + 1) % NUM_USERGART_ENTRIES;
492
493 return 0;
494}
495
496/**
497 * omap_gem_fault - pagefault handler for GEM objects
498 * @vma: the VMA of the GEM object
499 * @vmf: fault detail
500 *
501 * Invoked when a fault occurs on an mmap of a GEM managed area. GEM
502 * does most of the work for us including the actual map/unmap calls
503 * but we need to do the actual page work.
504 *
505 * The VMA was set up by GEM. In doing so it also ensured that the
506 * vma->vm_private_data points to the GEM object that is backing this
507 * mapping.
508 */
509int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
510{
511 struct drm_gem_object *obj = vma->vm_private_data;
512 struct omap_gem_object *omap_obj = to_omap_bo(obj);
513 struct drm_device *dev = obj->dev;
514 struct page **pages;
515 int ret;
516
517 /* Make sure we don't parallel update on a fault, nor move or remove
518 * something from beneath our feet
519 */
520 mutex_lock(&dev->struct_mutex);
521
522 /* if a shmem backed object, make sure we have pages attached now */
523 ret = get_pages(obj, &pages);
524 if (ret)
525 goto fail;
526
527 /* where should we do corresponding put_pages().. we are mapping
528 * the original page, rather than thru a GART, so we can't rely
529 * on eviction to trigger this. But munmap() or all mappings should
530 * probably trigger put_pages()?
531 */
532
533 if (omap_obj->flags & OMAP_BO_TILED)
534 ret = fault_2d(obj, vma, vmf);
535 else
536 ret = fault_1d(obj, vma, vmf);
537
538
539fail:
540 mutex_unlock(&dev->struct_mutex);
541 switch (ret) {
542 case 0:
543 case -ERESTARTSYS:
544 case -EINTR:
545 return VM_FAULT_NOPAGE;
546 case -ENOMEM:
547 return VM_FAULT_OOM;
548 default:
549 return VM_FAULT_SIGBUS;
550 }
551}
552
553/** We override mainly to fix up some of the vm mapping flags.. */
554int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma)
555{
556 int ret;
557
558 ret = drm_gem_mmap(filp, vma);
559 if (ret) {
560 DBG("mmap failed: %d", ret);
561 return ret;
562 }
563
564 return omap_gem_mmap_obj(vma->vm_private_data, vma);
565}
566
567int omap_gem_mmap_obj(struct drm_gem_object *obj,
568 struct vm_area_struct *vma)
569{
570 struct omap_gem_object *omap_obj = to_omap_bo(obj);
571
572 vma->vm_flags &= ~VM_PFNMAP;
573 vma->vm_flags |= VM_MIXEDMAP;
574
575 if (omap_obj->flags & OMAP_BO_WC) {
576 vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
577 } else if (omap_obj->flags & OMAP_BO_UNCACHED) {
578 vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
579 } else {
580 /*
581 * We do have some private objects, at least for scanout buffers
582 * on hardware without DMM/TILER. But these are allocated write-
583 * combine
584 */
585 if (WARN_ON(!obj->filp))
586 return -EINVAL;
587
588 /*
589 * Shunt off cached objs to shmem file so they have their own
590 * address_space (so unmap_mapping_range does what we want,
591 * in particular in the case of mmap'd dmabufs)
592 */
593 fput(vma->vm_file);
594 vma->vm_pgoff = 0;
595 vma->vm_file = get_file(obj->filp);
596
597 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
598 }
599
600 return 0;
601}
602
603
604/**
605 * omap_gem_dumb_create - create a dumb buffer
606 * @drm_file: our client file
607 * @dev: our device
608 * @args: the requested arguments copied from userspace
609 *
610 * Allocate a buffer suitable for use for a frame buffer of the
611 * form described by user space. Give userspace a handle by which
612 * to reference it.
613 */
614int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
615 struct drm_mode_create_dumb *args)
616{
617 union omap_gem_size gsize;
618
619 /* in case someone tries to feed us a completely bogus stride: */
620 args->pitch = align_pitch(args->pitch, args->width, args->bpp);
621 args->size = PAGE_ALIGN(args->pitch * args->height);
622
623 gsize = (union omap_gem_size){
624 .bytes = args->size,
625 };
626
627 return omap_gem_new_handle(dev, file, gsize,
628 OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle);
629}
630
631/**
632 * omap_gem_dumb_destroy - destroy a dumb buffer
633 * @file: client file
634 * @dev: our DRM device
635 * @handle: the object handle
636 *
637 * Destroy a handle that was created via omap_gem_dumb_create.
638 */
639int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
640 uint32_t handle)
641{
642 /* No special work needed, drop the reference and see what falls out */
643 return drm_gem_handle_delete(file, handle);
644}
645
646/**
647 * omap_gem_dumb_map - buffer mapping for dumb interface
648 * @file: our drm client file
649 * @dev: drm device
650 * @handle: GEM handle to the object (from dumb_create)
651 *
652 * Do the necessary setup to allow the mapping of the frame buffer
653 * into user memory. We don't have to do much here at the moment.
654 */
655int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
656 uint32_t handle, uint64_t *offset)
657{
658 struct drm_gem_object *obj;
659 int ret = 0;
660
661 /* GEM does all our handle to object mapping */
662 obj = drm_gem_object_lookup(dev, file, handle);
663 if (obj == NULL) {
664 ret = -ENOENT;
665 goto fail;
666 }
667
668 *offset = omap_gem_mmap_offset(obj);
669
670 drm_gem_object_unreference_unlocked(obj);
671
672fail:
673 return ret;
674}
675
676/* Set scrolling position. This allows us to implement fast scrolling
677 * for console.
678 *
679 * Call only from non-atomic contexts.
680 */
681int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll)
682{
683 struct omap_gem_object *omap_obj = to_omap_bo(obj);
684 uint32_t npages = obj->size >> PAGE_SHIFT;
685 int ret = 0;
686
687 if (roll > npages) {
688 dev_err(obj->dev->dev, "invalid roll: %d\n", roll);
689 return -EINVAL;
690 }
691
692 omap_obj->roll = roll;
693
694 mutex_lock(&obj->dev->struct_mutex);
695
696 /* if we aren't mapped yet, we don't need to do anything */
697 if (omap_obj->block) {
698 struct page **pages;
699 ret = get_pages(obj, &pages);
700 if (ret)
701 goto fail;
702 ret = tiler_pin(omap_obj->block, pages, npages, roll, true);
703 if (ret)
704 dev_err(obj->dev->dev, "could not repin: %d\n", ret);
705 }
706
707fail:
708 mutex_unlock(&obj->dev->struct_mutex);
709
710 return ret;
711}
712
713/* Sync the buffer for CPU access.. note pages should already be
714 * attached, ie. omap_gem_get_pages()
715 */
716void omap_gem_cpu_sync(struct drm_gem_object *obj, int pgoff)
717{
718 struct drm_device *dev = obj->dev;
719 struct omap_gem_object *omap_obj = to_omap_bo(obj);
720
721 if (is_cached_coherent(obj) && omap_obj->addrs[pgoff]) {
722 dma_unmap_page(dev->dev, omap_obj->addrs[pgoff],
723 PAGE_SIZE, DMA_BIDIRECTIONAL);
724 omap_obj->addrs[pgoff] = 0;
725 }
726}
727
728/* sync the buffer for DMA access */
729void omap_gem_dma_sync(struct drm_gem_object *obj,
730 enum dma_data_direction dir)
731{
732 struct drm_device *dev = obj->dev;
733 struct omap_gem_object *omap_obj = to_omap_bo(obj);
734
735 if (is_cached_coherent(obj)) {
736 int i, npages = obj->size >> PAGE_SHIFT;
737 struct page **pages = omap_obj->pages;
738 bool dirty = false;
739
740 for (i = 0; i < npages; i++) {
741 if (!omap_obj->addrs[i]) {
742 omap_obj->addrs[i] = dma_map_page(dev->dev, pages[i], 0,
743 PAGE_SIZE, DMA_BIDIRECTIONAL);
744 dirty = true;
745 }
746 }
747
748 if (dirty) {
749 unmap_mapping_range(obj->filp->f_mapping, 0,
750 omap_gem_mmap_size(obj), 1);
751 }
752 }
753}
754
755/* Get physical address for DMA.. if 'remap' is true, and the buffer is not
756 * already contiguous, remap it to pin in physically contiguous memory.. (ie.
757 * map in TILER)
758 */
759int omap_gem_get_paddr(struct drm_gem_object *obj,
760 dma_addr_t *paddr, bool remap)
761{
762 struct omap_drm_private *priv = obj->dev->dev_private;
763 struct omap_gem_object *omap_obj = to_omap_bo(obj);
764 int ret = 0;
765
766 mutex_lock(&obj->dev->struct_mutex);
767
768 if (remap && is_shmem(obj) && priv->has_dmm) {
769 if (omap_obj->paddr_cnt == 0) {
770 struct page **pages;
771 uint32_t npages = obj->size >> PAGE_SHIFT;
772 enum tiler_fmt fmt = gem2fmt(omap_obj->flags);
773 struct tiler_block *block;
774
775 BUG_ON(omap_obj->block);
776
777 ret = get_pages(obj, &pages);
778 if (ret)
779 goto fail;
780
781 if (omap_obj->flags & OMAP_BO_TILED) {
782 block = tiler_reserve_2d(fmt,
783 omap_obj->width,
784 omap_obj->height, 0);
785 } else {
786 block = tiler_reserve_1d(obj->size);
787 }
788
789 if (IS_ERR(block)) {
790 ret = PTR_ERR(block);
791 dev_err(obj->dev->dev,
792 "could not remap: %d (%d)\n", ret, fmt);
793 goto fail;
794 }
795
796 /* TODO: enable async refill.. */
797 ret = tiler_pin(block, pages, npages,
798 omap_obj->roll, true);
799 if (ret) {
800 tiler_release(block);
801 dev_err(obj->dev->dev,
802 "could not pin: %d\n", ret);
803 goto fail;
804 }
805
806 omap_obj->paddr = tiler_ssptr(block);
807 omap_obj->block = block;
808
809 DBG("got paddr: %08x", omap_obj->paddr);
810 }
811
812 omap_obj->paddr_cnt++;
813
814 *paddr = omap_obj->paddr;
815 } else if (omap_obj->flags & OMAP_BO_DMA) {
816 *paddr = omap_obj->paddr;
817 } else {
818 ret = -EINVAL;
819 goto fail;
820 }
821
822fail:
823 mutex_unlock(&obj->dev->struct_mutex);
824
825 return ret;
826}
827
828/* Release physical address, when DMA is no longer being performed.. this
829 * could potentially unpin and unmap buffers from TILER
830 */
831int omap_gem_put_paddr(struct drm_gem_object *obj)
832{
833 struct omap_gem_object *omap_obj = to_omap_bo(obj);
834 int ret = 0;
835
836 mutex_lock(&obj->dev->struct_mutex);
837 if (omap_obj->paddr_cnt > 0) {
838 omap_obj->paddr_cnt--;
839 if (omap_obj->paddr_cnt == 0) {
840 ret = tiler_unpin(omap_obj->block);
841 if (ret) {
842 dev_err(obj->dev->dev,
843 "could not unpin pages: %d\n", ret);
844 goto fail;
845 }
846 ret = tiler_release(omap_obj->block);
847 if (ret) {
848 dev_err(obj->dev->dev,
849 "could not release unmap: %d\n", ret);
850 }
851 omap_obj->block = NULL;
852 }
853 }
854fail:
855 mutex_unlock(&obj->dev->struct_mutex);
856 return ret;
857}
858
859/* Get rotated scanout address (only valid if already pinned), at the
860 * specified orientation and x,y offset from top-left corner of buffer
861 * (only valid for tiled 2d buffers)
862 */
863int omap_gem_rotated_paddr(struct drm_gem_object *obj, uint32_t orient,
864 int x, int y, dma_addr_t *paddr)
865{
866 struct omap_gem_object *omap_obj = to_omap_bo(obj);
867 int ret = -EINVAL;
868
869 mutex_lock(&obj->dev->struct_mutex);
870 if ((omap_obj->paddr_cnt > 0) && omap_obj->block &&
871 (omap_obj->flags & OMAP_BO_TILED)) {
872 *paddr = tiler_tsptr(omap_obj->block, orient, x, y);
873 ret = 0;
874 }
875 mutex_unlock(&obj->dev->struct_mutex);
876 return ret;
877}
878
879/* Get tiler stride for the buffer (only valid for 2d tiled buffers) */
880int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient)
881{
882 struct omap_gem_object *omap_obj = to_omap_bo(obj);
883 int ret = -EINVAL;
884 if (omap_obj->flags & OMAP_BO_TILED)
885 ret = tiler_stride(gem2fmt(omap_obj->flags), orient);
886 return ret;
887}
888
889/* acquire pages when needed (for example, for DMA where physically
890 * contiguous buffer is not required
891 */
892static int get_pages(struct drm_gem_object *obj, struct page ***pages)
893{
894 struct omap_gem_object *omap_obj = to_omap_bo(obj);
895 int ret = 0;
896
897 if (is_shmem(obj) && !omap_obj->pages) {
898 ret = omap_gem_attach_pages(obj);
899 if (ret) {
900 dev_err(obj->dev->dev, "could not attach pages\n");
901 return ret;
902 }
903 }
904
905 /* TODO: even phys-contig.. we should have a list of pages? */
906 *pages = omap_obj->pages;
907
908 return 0;
909}
910
911/* if !remap, and we don't have pages backing, then fail, rather than
912 * increasing the pin count (which we don't really do yet anyways,
913 * because we don't support swapping pages back out). And 'remap'
914 * might not be quite the right name, but I wanted to keep it working
915 * similarly to omap_gem_get_paddr(). Note though that mutex is not
916 * aquired if !remap (because this can be called in atomic ctxt),
917 * but probably omap_gem_get_paddr() should be changed to work in the
918 * same way. If !remap, a matching omap_gem_put_pages() call is not
919 * required (and should not be made).
920 */
921int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages,
922 bool remap)
923{
924 int ret;
925 if (!remap) {
926 struct omap_gem_object *omap_obj = to_omap_bo(obj);
927 if (!omap_obj->pages)
928 return -ENOMEM;
929 *pages = omap_obj->pages;
930 return 0;
931 }
932 mutex_lock(&obj->dev->struct_mutex);
933 ret = get_pages(obj, pages);
934 mutex_unlock(&obj->dev->struct_mutex);
935 return ret;
936}
937
938/* release pages when DMA no longer being performed */
939int omap_gem_put_pages(struct drm_gem_object *obj)
940{
941 /* do something here if we dynamically attach/detach pages.. at
942 * least they would no longer need to be pinned if everyone has
943 * released the pages..
944 */
945 return 0;
946}
947
948/* Get kernel virtual address for CPU access.. this more or less only
949 * exists for omap_fbdev. This should be called with struct_mutex
950 * held.
951 */
952void *omap_gem_vaddr(struct drm_gem_object *obj)
953{
954 struct omap_gem_object *omap_obj = to_omap_bo(obj);
955 WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
956 if (!omap_obj->vaddr) {
957 struct page **pages;
958 int ret = get_pages(obj, &pages);
959 if (ret)
960 return ERR_PTR(ret);
961 omap_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
962 VM_MAP, pgprot_writecombine(PAGE_KERNEL));
963 }
964 return omap_obj->vaddr;
965}
966
967#ifdef CONFIG_PM
968/* re-pin objects in DMM in resume path: */
969int omap_gem_resume(struct device *dev)
970{
971 struct drm_device *drm_dev = dev_get_drvdata(dev);
972 struct omap_drm_private *priv = drm_dev->dev_private;
973 struct omap_gem_object *omap_obj;
974 int ret = 0;
975
976 list_for_each_entry(omap_obj, &priv->obj_list, mm_list) {
977 if (omap_obj->block) {
978 struct drm_gem_object *obj = &omap_obj->base;
979 uint32_t npages = obj->size >> PAGE_SHIFT;
980 WARN_ON(!omap_obj->pages); /* this can't happen */
981 ret = tiler_pin(omap_obj->block,
982 omap_obj->pages, npages,
983 omap_obj->roll, true);
984 if (ret) {
985 dev_err(dev, "could not repin: %d\n", ret);
986 return ret;
987 }
988 }
989 }
990
991 return 0;
992}
993#endif
994
995#ifdef CONFIG_DEBUG_FS
996void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
997{
998 struct drm_device *dev = obj->dev;
999 struct omap_gem_object *omap_obj = to_omap_bo(obj);
1000 uint64_t off = 0;
1001
1002 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
1003
1004 if (obj->map_list.map)
1005 off = (uint64_t)obj->map_list.hash.key;
1006
1007 seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
1008 omap_obj->flags, obj->name, obj->refcount.refcount.counter,
1009 off, omap_obj->paddr, omap_obj->paddr_cnt,
1010 omap_obj->vaddr, omap_obj->roll);
1011
1012 if (omap_obj->flags & OMAP_BO_TILED) {
1013 seq_printf(m, " %dx%d", omap_obj->width, omap_obj->height);
1014 if (omap_obj->block) {
1015 struct tcm_area *area = &omap_obj->block->area;
1016 seq_printf(m, " (%dx%d, %dx%d)",
1017 area->p0.x, area->p0.y,
1018 area->p1.x, area->p1.y);
1019 }
1020 } else {
1021 seq_printf(m, " %d", obj->size);
1022 }
1023
1024 seq_printf(m, "\n");
1025}
1026
1027void omap_gem_describe_objects(struct list_head *list, struct seq_file *m)
1028{
1029 struct omap_gem_object *omap_obj;
1030 int count = 0;
1031 size_t size = 0;
1032
1033 list_for_each_entry(omap_obj, list, mm_list) {
1034 struct drm_gem_object *obj = &omap_obj->base;
1035 seq_printf(m, " ");
1036 omap_gem_describe(obj, m);
1037 count++;
1038 size += obj->size;
1039 }
1040
1041 seq_printf(m, "Total %d objects, %zu bytes\n", count, size);
1042}
1043#endif
1044
1045/* Buffer Synchronization:
1046 */
1047
1048struct omap_gem_sync_waiter {
1049 struct list_head list;
1050 struct omap_gem_object *omap_obj;
1051 enum omap_gem_op op;
1052 uint32_t read_target, write_target;
1053 /* notify called w/ sync_lock held */
1054 void (*notify)(void *arg);
1055 void *arg;
1056};
1057
1058/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
1059 * the read and/or write target count is achieved which can call a user
1060 * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
1061 * cpu access), etc.
1062 */
1063static LIST_HEAD(waiters);
1064
1065static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
1066{
1067 struct omap_gem_object *omap_obj = waiter->omap_obj;
1068 if ((waiter->op & OMAP_GEM_READ) &&
1069 (omap_obj->sync->read_complete < waiter->read_target))
1070 return true;
1071 if ((waiter->op & OMAP_GEM_WRITE) &&
1072 (omap_obj->sync->write_complete < waiter->write_target))
1073 return true;
1074 return false;
1075}
1076
1077/* macro for sync debug.. */
1078#define SYNCDBG 0
1079#define SYNC(fmt, ...) do { if (SYNCDBG) \
1080 printk(KERN_ERR "%s:%d: "fmt"\n", \
1081 __func__, __LINE__, ##__VA_ARGS__); \
1082 } while (0)
1083
1084
1085static void sync_op_update(void)
1086{
1087 struct omap_gem_sync_waiter *waiter, *n;
1088 list_for_each_entry_safe(waiter, n, &waiters, list) {
1089 if (!is_waiting(waiter)) {
1090 list_del(&waiter->list);
1091 SYNC("notify: %p", waiter);
1092 waiter->notify(waiter->arg);
1093 kfree(waiter);
1094 }
1095 }
1096}
1097
1098static inline int sync_op(struct drm_gem_object *obj,
1099 enum omap_gem_op op, bool start)
1100{
1101 struct omap_gem_object *omap_obj = to_omap_bo(obj);
1102 int ret = 0;
1103
1104 spin_lock(&sync_lock);
1105
1106 if (!omap_obj->sync) {
1107 omap_obj->sync = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC);
1108 if (!omap_obj->sync) {
1109 ret = -ENOMEM;
1110 goto unlock;
1111 }
1112 }
1113
1114 if (start) {
1115 if (op & OMAP_GEM_READ)
1116 omap_obj->sync->read_pending++;
1117 if (op & OMAP_GEM_WRITE)
1118 omap_obj->sync->write_pending++;
1119 } else {
1120 if (op & OMAP_GEM_READ)
1121 omap_obj->sync->read_complete++;
1122 if (op & OMAP_GEM_WRITE)
1123 omap_obj->sync->write_complete++;
1124 sync_op_update();
1125 }
1126
1127unlock:
1128 spin_unlock(&sync_lock);
1129
1130 return ret;
1131}
1132
1133/* it is a bit lame to handle updates in this sort of polling way, but
1134 * in case of PVR, the GPU can directly update read/write complete
1135 * values, and not really tell us which ones it updated.. this also
1136 * means that sync_lock is not quite sufficient. So we'll need to
1137 * do something a bit better when it comes time to add support for
1138 * separate 2d hw..
1139 */
1140void omap_gem_op_update(void)
1141{
1142 spin_lock(&sync_lock);
1143 sync_op_update();
1144 spin_unlock(&sync_lock);
1145}
1146
1147/* mark the start of read and/or write operation */
1148int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op)
1149{
1150 return sync_op(obj, op, true);
1151}
1152
1153int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op)
1154{
1155 return sync_op(obj, op, false);
1156}
1157
1158static DECLARE_WAIT_QUEUE_HEAD(sync_event);
1159
1160static void sync_notify(void *arg)
1161{
1162 struct task_struct **waiter_task = arg;
1163 *waiter_task = NULL;
1164 wake_up_all(&sync_event);
1165}
1166
1167int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
1168{
1169 struct omap_gem_object *omap_obj = to_omap_bo(obj);
1170 int ret = 0;
1171 if (omap_obj->sync) {
1172 struct task_struct *waiter_task = current;
1173 struct omap_gem_sync_waiter *waiter =
1174 kzalloc(sizeof(*waiter), GFP_KERNEL);
1175
1176 if (!waiter)
1177 return -ENOMEM;
1178
1179 waiter->omap_obj = omap_obj;
1180 waiter->op = op;
1181 waiter->read_target = omap_obj->sync->read_pending;
1182 waiter->write_target = omap_obj->sync->write_pending;
1183 waiter->notify = sync_notify;
1184 waiter->arg = &waiter_task;
1185
1186 spin_lock(&sync_lock);
1187 if (is_waiting(waiter)) {
1188 SYNC("waited: %p", waiter);
1189 list_add_tail(&waiter->list, &waiters);
1190 spin_unlock(&sync_lock);
1191 ret = wait_event_interruptible(sync_event,
1192 (waiter_task == NULL));
1193 spin_lock(&sync_lock);
1194 if (waiter_task) {
1195 SYNC("interrupted: %p", waiter);
1196 /* we were interrupted */
1197 list_del(&waiter->list);
1198 waiter_task = NULL;
1199 } else {
1200 /* freed in sync_op_update() */
1201 waiter = NULL;
1202 }
1203 }
1204 spin_unlock(&sync_lock);
1205
1206 if (waiter)
1207 kfree(waiter);
1208 }
1209 return ret;
1210}
1211
1212/* call fxn(arg), either synchronously or asynchronously if the op
1213 * is currently blocked.. fxn() can be called from any context
1214 *
1215 * (TODO for now fxn is called back from whichever context calls
1216 * omap_gem_op_update().. but this could be better defined later
1217 * if needed)
1218 *
1219 * TODO more code in common w/ _sync()..
1220 */
1221int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
1222 void (*fxn)(void *arg), void *arg)
1223{
1224 struct omap_gem_object *omap_obj = to_omap_bo(obj);
1225 if (omap_obj->sync) {
1226 struct omap_gem_sync_waiter *waiter =
1227 kzalloc(sizeof(*waiter), GFP_ATOMIC);
1228
1229 if (!waiter)
1230 return -ENOMEM;
1231
1232 waiter->omap_obj = omap_obj;
1233 waiter->op = op;
1234 waiter->read_target = omap_obj->sync->read_pending;
1235 waiter->write_target = omap_obj->sync->write_pending;
1236 waiter->notify = fxn;
1237 waiter->arg = arg;
1238
1239 spin_lock(&sync_lock);
1240 if (is_waiting(waiter)) {
1241 SYNC("waited: %p", waiter);
1242 list_add_tail(&waiter->list, &waiters);
1243 spin_unlock(&sync_lock);
1244 return 0;
1245 }
1246
1247 spin_unlock(&sync_lock);
1248 }
1249
1250 /* no waiting.. */
1251 fxn(arg);
1252
1253 return 0;
1254}
1255
1256/* special API so PVR can update the buffer to use a sync-object allocated
1257 * from it's sync-obj heap. Only used for a newly allocated (from PVR's
1258 * perspective) sync-object, so we overwrite the new syncobj w/ values
1259 * from the already allocated syncobj (if there is one)
1260 */
1261int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
1262{
1263 struct omap_gem_object *omap_obj = to_omap_bo(obj);
1264 int ret = 0;
1265
1266 spin_lock(&sync_lock);
1267
1268 if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
1269 /* clearing a previously set syncobj */
1270 syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync),
1271 GFP_ATOMIC);
1272 if (!syncobj) {
1273 ret = -ENOMEM;
1274 goto unlock;
1275 }
1276 omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
1277 omap_obj->sync = syncobj;
1278 } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
1279 /* replacing an existing syncobj */
1280 if (omap_obj->sync) {
1281 memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
1282 kfree(omap_obj->sync);
1283 }
1284 omap_obj->flags |= OMAP_BO_EXT_SYNC;
1285 omap_obj->sync = syncobj;
1286 }
1287
1288unlock:
1289 spin_unlock(&sync_lock);
1290 return ret;
1291}
1292
1293int omap_gem_init_object(struct drm_gem_object *obj)
1294{
1295 return -EINVAL; /* unused */
1296}
1297
1298/* don't call directly.. called from GEM core when it is time to actually
1299 * free the object..
1300 */
1301void omap_gem_free_object(struct drm_gem_object *obj)
1302{
1303 struct drm_device *dev = obj->dev;
1304 struct omap_gem_object *omap_obj = to_omap_bo(obj);
1305
1306 evict(obj);
1307
1308 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
1309
1310 list_del(&omap_obj->mm_list);
1311
1312 if (obj->map_list.map)
1313 drm_gem_free_mmap_offset(obj);
1314
1315 /* this means the object is still pinned.. which really should
1316 * not happen. I think..
1317 */
1318 WARN_ON(omap_obj->paddr_cnt > 0);
1319
1320 /* don't free externally allocated backing memory */
1321 if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
1322 if (omap_obj->pages)
1323 omap_gem_detach_pages(obj);
1324
1325 if (!is_shmem(obj)) {
1326 dma_free_writecombine(dev->dev, obj->size,
1327 omap_obj->vaddr, omap_obj->paddr);
1328 } else if (omap_obj->vaddr) {
1329 vunmap(omap_obj->vaddr);
1330 }
1331 }
1332
1333 /* don't free externally allocated syncobj */
1334 if (!(omap_obj->flags & OMAP_BO_EXT_SYNC))
1335 kfree(omap_obj->sync);
1336
1337 drm_gem_object_release(obj);
1338
1339 kfree(obj);
1340}
1341
1342/* convenience method to construct a GEM buffer object, and userspace handle */
1343int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
1344 union omap_gem_size gsize, uint32_t flags, uint32_t *handle)
1345{
1346 struct drm_gem_object *obj;
1347 int ret;
1348
1349 obj = omap_gem_new(dev, gsize, flags);
1350 if (!obj)
1351 return -ENOMEM;
1352
1353 ret = drm_gem_handle_create(file, obj, handle);
1354 if (ret) {
1355 drm_gem_object_release(obj);
1356 kfree(obj); /* TODO isn't there a dtor to call? just copying i915 */
1357 return ret;
1358 }
1359
1360 /* drop reference from allocate - handle holds it now */
1361 drm_gem_object_unreference_unlocked(obj);
1362
1363 return 0;
1364}
1365
1366/* GEM buffer object constructor */
1367struct drm_gem_object *omap_gem_new(struct drm_device *dev,
1368 union omap_gem_size gsize, uint32_t flags)
1369{
1370 struct omap_drm_private *priv = dev->dev_private;
1371 struct omap_gem_object *omap_obj;
1372 struct drm_gem_object *obj = NULL;
1373 size_t size;
1374 int ret;
1375
1376 if (flags & OMAP_BO_TILED) {
1377 if (!usergart) {
1378 dev_err(dev->dev, "Tiled buffers require DMM\n");
1379 goto fail;
1380 }
1381
1382 /* tiled buffers are always shmem paged backed.. when they are
1383 * scanned out, they are remapped into DMM/TILER
1384 */
1385 flags &= ~OMAP_BO_SCANOUT;
1386
1387 /* currently don't allow cached buffers.. there is some caching
1388 * stuff that needs to be handled better
1389 */
1390 flags &= ~(OMAP_BO_CACHED|OMAP_BO_UNCACHED);
1391 flags |= OMAP_BO_WC;
1392
1393 /* align dimensions to slot boundaries... */
1394 tiler_align(gem2fmt(flags),
1395 &gsize.tiled.width, &gsize.tiled.height);
1396
1397 /* ...and calculate size based on aligned dimensions */
1398 size = tiler_size(gem2fmt(flags),
1399 gsize.tiled.width, gsize.tiled.height);
1400 } else {
1401 size = PAGE_ALIGN(gsize.bytes);
1402 }
1403
1404 omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL);
1405 if (!omap_obj) {
1406 dev_err(dev->dev, "could not allocate GEM object\n");
1407 goto fail;
1408 }
1409
1410 list_add(&omap_obj->mm_list, &priv->obj_list);
1411
1412 obj = &omap_obj->base;
1413
1414 if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) {
1415 /* attempt to allocate contiguous memory if we don't
1416 * have DMM for remappign discontiguous buffers
1417 */
1418 omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size,
1419 &omap_obj->paddr, GFP_KERNEL);
1420 if (omap_obj->vaddr)
1421 flags |= OMAP_BO_DMA;
1422
1423 }
1424
1425 omap_obj->flags = flags;
1426
1427 if (flags & OMAP_BO_TILED) {
1428 omap_obj->width = gsize.tiled.width;
1429 omap_obj->height = gsize.tiled.height;
1430 }
1431
1432 if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM))
1433 ret = drm_gem_private_object_init(dev, obj, size);
1434 else
1435 ret = drm_gem_object_init(dev, obj, size);
1436
1437 if (ret)
1438 goto fail;
1439
1440 return obj;
1441
1442fail:
1443 if (obj)
1444 omap_gem_free_object(obj);
1445
1446 return NULL;
1447}
1448
1449/* init/cleanup.. if DMM is used, we need to set some stuff up.. */
1450void omap_gem_init(struct drm_device *dev)
1451{
1452 struct omap_drm_private *priv = dev->dev_private;
1453 const enum tiler_fmt fmts[] = {
1454 TILFMT_8BIT, TILFMT_16BIT, TILFMT_32BIT
1455 };
1456 int i, j;
1457
1458 if (!dmm_is_available()) {
1459 /* DMM only supported on OMAP4 and later, so this isn't fatal */
1460 dev_warn(dev->dev, "DMM not available, disable DMM support\n");
1461 return;
1462 }
1463
1464 usergart = kzalloc(3 * sizeof(*usergart), GFP_KERNEL);
1465 if (!usergart) {
1466 dev_warn(dev->dev, "could not allocate usergart\n");
1467 return;
1468 }
1469
1470 /* reserve 4k aligned/wide regions for userspace mappings: */
1471 for (i = 0; i < ARRAY_SIZE(fmts); i++) {
1472 uint16_t h = 1, w = PAGE_SIZE >> i;
1473 tiler_align(fmts[i], &w, &h);
1474 /* note: since each region is 1 4kb page wide, and minimum
1475 * number of rows, the height ends up being the same as the
1476 * # of pages in the region
1477 */
1478 usergart[i].height = h;
1479 usergart[i].height_shift = ilog2(h);
1480 usergart[i].stride_pfn = tiler_stride(fmts[i], 0) >> PAGE_SHIFT;
1481 usergart[i].slot_shift = ilog2((PAGE_SIZE / h) >> i);
1482 for (j = 0; j < NUM_USERGART_ENTRIES; j++) {
1483 struct usergart_entry *entry = &usergart[i].entry[j];
1484 struct tiler_block *block =
1485 tiler_reserve_2d(fmts[i], w, h,
1486 PAGE_SIZE);
1487 if (IS_ERR(block)) {
1488 dev_err(dev->dev,
1489 "reserve failed: %d, %d, %ld\n",
1490 i, j, PTR_ERR(block));
1491 return;
1492 }
1493 entry->paddr = tiler_ssptr(block);
1494 entry->block = block;
1495
1496 DBG("%d:%d: %dx%d: paddr=%08x stride=%d", i, j, w, h,
1497 entry->paddr,
1498 usergart[i].stride_pfn << PAGE_SHIFT);
1499 }
1500 }
1501
1502 priv->has_dmm = true;
1503}
1504
1505void omap_gem_deinit(struct drm_device *dev)
1506{
1507 /* I believe we can rely on there being no more outstanding GEM
1508 * objects which could depend on usergart/dmm at this point.
1509 */
1510 kfree(usergart);
1511}
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
new file mode 100644
index 000000000000..ac74d1bc67bf
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
@@ -0,0 +1,225 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include <linux/dma-buf.h>
23
24static struct sg_table *omap_gem_map_dma_buf(
25 struct dma_buf_attachment *attachment,
26 enum dma_data_direction dir)
27{
28 struct drm_gem_object *obj = attachment->dmabuf->priv;
29 struct sg_table *sg;
30 dma_addr_t paddr;
31 int ret;
32
33 sg = kzalloc(sizeof(*sg), GFP_KERNEL);
34 if (!sg)
35 return ERR_PTR(-ENOMEM);
36
37 /* camera, etc, need physically contiguous.. but we need a
38 * better way to know this..
39 */
40 ret = omap_gem_get_paddr(obj, &paddr, true);
41 if (ret)
42 goto out;
43
44 ret = sg_alloc_table(sg, 1, GFP_KERNEL);
45 if (ret)
46 goto out;
47
48 sg_init_table(sg->sgl, 1);
49 sg_dma_len(sg->sgl) = obj->size;
50 sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(paddr)), obj->size, 0);
51 sg_dma_address(sg->sgl) = paddr;
52
53 /* this should be after _get_paddr() to ensure we have pages attached */
54 omap_gem_dma_sync(obj, dir);
55
56 return sg;
57out:
58 kfree(sg);
59 return ERR_PTR(ret);
60}
61
62static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
63 struct sg_table *sg, enum dma_data_direction dir)
64{
65 struct drm_gem_object *obj = attachment->dmabuf->priv;
66 omap_gem_put_paddr(obj);
67 sg_free_table(sg);
68 kfree(sg);
69}
70
71static void omap_gem_dmabuf_release(struct dma_buf *buffer)
72{
73 struct drm_gem_object *obj = buffer->priv;
74 /* release reference that was taken when dmabuf was exported
75 * in omap_gem_prime_set()..
76 */
77 drm_gem_object_unreference_unlocked(obj);
78}
79
80
81static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer,
82 size_t start, size_t len, enum dma_data_direction dir)
83{
84 struct drm_gem_object *obj = buffer->priv;
85 struct page **pages;
86 if (omap_gem_flags(obj) & OMAP_BO_TILED) {
87 /* TODO we would need to pin at least part of the buffer to
88 * get de-tiled view. For now just reject it.
89 */
90 return -ENOMEM;
91 }
92 /* make sure we have the pages: */
93 return omap_gem_get_pages(obj, &pages, true);
94}
95
96static void omap_gem_dmabuf_end_cpu_access(struct dma_buf *buffer,
97 size_t start, size_t len, enum dma_data_direction dir)
98{
99 struct drm_gem_object *obj = buffer->priv;
100 omap_gem_put_pages(obj);
101}
102
103
104static void *omap_gem_dmabuf_kmap_atomic(struct dma_buf *buffer,
105 unsigned long page_num)
106{
107 struct drm_gem_object *obj = buffer->priv;
108 struct page **pages;
109 omap_gem_get_pages(obj, &pages, false);
110 omap_gem_cpu_sync(obj, page_num);
111 return kmap_atomic(pages[page_num]);
112}
113
114static void omap_gem_dmabuf_kunmap_atomic(struct dma_buf *buffer,
115 unsigned long page_num, void *addr)
116{
117 kunmap_atomic(addr);
118}
119
120static void *omap_gem_dmabuf_kmap(struct dma_buf *buffer,
121 unsigned long page_num)
122{
123 struct drm_gem_object *obj = buffer->priv;
124 struct page **pages;
125 omap_gem_get_pages(obj, &pages, false);
126 omap_gem_cpu_sync(obj, page_num);
127 return kmap(pages[page_num]);
128}
129
130static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer,
131 unsigned long page_num, void *addr)
132{
133 struct drm_gem_object *obj = buffer->priv;
134 struct page **pages;
135 omap_gem_get_pages(obj, &pages, false);
136 kunmap(pages[page_num]);
137}
138
139/*
140 * TODO maybe we can split up drm_gem_mmap to avoid duplicating
141 * some here.. or at least have a drm_dmabuf_mmap helper.
142 */
143static int omap_gem_dmabuf_mmap(struct dma_buf *buffer,
144 struct vm_area_struct *vma)
145{
146 struct drm_gem_object *obj = buffer->priv;
147 int ret = 0;
148
149 if (WARN_ON(!obj->filp))
150 return -EINVAL;
151
152 /* Check for valid size. */
153 if (omap_gem_mmap_size(obj) < vma->vm_end - vma->vm_start) {
154 ret = -EINVAL;
155 goto out_unlock;
156 }
157
158 if (!obj->dev->driver->gem_vm_ops) {
159 ret = -EINVAL;
160 goto out_unlock;
161 }
162
163 vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
164 vma->vm_ops = obj->dev->driver->gem_vm_ops;
165 vma->vm_private_data = obj;
166 vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
167
168 /* Take a ref for this mapping of the object, so that the fault
169 * handler can dereference the mmap offset's pointer to the object.
170 * This reference is cleaned up by the corresponding vm_close
171 * (which should happen whether the vma was created by this call, or
172 * by a vm_open due to mremap or partial unmap or whatever).
173 */
174 vma->vm_ops->open(vma);
175
176out_unlock:
177
178 return omap_gem_mmap_obj(obj, vma);
179}
180
181struct dma_buf_ops omap_dmabuf_ops = {
182 .map_dma_buf = omap_gem_map_dma_buf,
183 .unmap_dma_buf = omap_gem_unmap_dma_buf,
184 .release = omap_gem_dmabuf_release,
185 .begin_cpu_access = omap_gem_dmabuf_begin_cpu_access,
186 .end_cpu_access = omap_gem_dmabuf_end_cpu_access,
187 .kmap_atomic = omap_gem_dmabuf_kmap_atomic,
188 .kunmap_atomic = omap_gem_dmabuf_kunmap_atomic,
189 .kmap = omap_gem_dmabuf_kmap,
190 .kunmap = omap_gem_dmabuf_kunmap,
191 .mmap = omap_gem_dmabuf_mmap,
192};
193
194struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
195 struct drm_gem_object *obj, int flags)
196{
197 return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags);
198}
199
200struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
201 struct dma_buf *buffer)
202{
203 struct drm_gem_object *obj;
204
205 /* is this one of own objects? */
206 if (buffer->ops == &omap_dmabuf_ops) {
207 obj = buffer->priv;
208 /* is it from our device? */
209 if (obj->dev == dev) {
210 /*
211 * Importing dmabuf exported from out own gem increases
212 * refcount on gem itself instead of f_count of dmabuf.
213 */
214 drm_gem_object_reference(obj);
215 dma_buf_put(buffer);
216 return obj;
217 }
218 }
219
220 /*
221 * TODO add support for importing buffers from other devices..
222 * for now we don't need this but would be nice to add eventually
223 */
224 return ERR_PTR(-EINVAL);
225}
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
new file mode 100644
index 000000000000..e4a66a35fc6a
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
@@ -0,0 +1,169 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_gem_helpers.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/* temporary copy of drm_gem_{get,put}_pages() until the
21 * "drm/gem: add functions to get/put pages" patch is merged..
22 */
23
24#include <linux/module.h>
25#include <linux/types.h>
26#include <linux/shmem_fs.h>
27
28#include <drm/drmP.h>
29
30/**
31 * drm_gem_get_pages - helper to allocate backing pages for a GEM object
32 * @obj: obj in question
33 * @gfpmask: gfp mask of requested pages
34 */
35struct page **_drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
36{
37 struct inode *inode;
38 struct address_space *mapping;
39 struct page *p, **pages;
40 int i, npages;
41
42 /* This is the shared memory object that backs the GEM resource */
43 inode = obj->filp->f_path.dentry->d_inode;
44 mapping = inode->i_mapping;
45
46 npages = obj->size >> PAGE_SHIFT;
47
48 pages = drm_malloc_ab(npages, sizeof(struct page *));
49 if (pages == NULL)
50 return ERR_PTR(-ENOMEM);
51
52 gfpmask |= mapping_gfp_mask(mapping);
53
54 for (i = 0; i < npages; i++) {
55 p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
56 if (IS_ERR(p))
57 goto fail;
58 pages[i] = p;
59
60 /* There is a hypothetical issue w/ drivers that require
61 * buffer memory in the low 4GB.. if the pages are un-
62 * pinned, and swapped out, they can end up swapped back
63 * in above 4GB. If pages are already in memory, then
64 * shmem_read_mapping_page_gfp will ignore the gfpmask,
65 * even if the already in-memory page disobeys the mask.
66 *
67 * It is only a theoretical issue today, because none of
68 * the devices with this limitation can be populated with
69 * enough memory to trigger the issue. But this BUG_ON()
70 * is here as a reminder in case the problem with
71 * shmem_read_mapping_page_gfp() isn't solved by the time
72 * it does become a real issue.
73 *
74 * See this thread: http://lkml.org/lkml/2011/7/11/238
75 */
76 BUG_ON((gfpmask & __GFP_DMA32) &&
77 (page_to_pfn(p) >= 0x00100000UL));
78 }
79
80 return pages;
81
82fail:
83 while (i--)
84 page_cache_release(pages[i]);
85
86 drm_free_large(pages);
87 return ERR_CAST(p);
88}
89
90/**
91 * drm_gem_put_pages - helper to free backing pages for a GEM object
92 * @obj: obj in question
93 * @pages: pages to free
94 */
95void _drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages,
96 bool dirty, bool accessed)
97{
98 int i, npages;
99
100 npages = obj->size >> PAGE_SHIFT;
101
102 for (i = 0; i < npages; i++) {
103 if (dirty)
104 set_page_dirty(pages[i]);
105
106 if (accessed)
107 mark_page_accessed(pages[i]);
108
109 /* Undo the reference we took when populating the table */
110 page_cache_release(pages[i]);
111 }
112
113 drm_free_large(pages);
114}
115
116int
117_drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
118{
119 struct drm_device *dev = obj->dev;
120 struct drm_gem_mm *mm = dev->mm_private;
121 struct drm_map_list *list;
122 struct drm_local_map *map;
123 int ret = 0;
124
125 /* Set the object up for mmap'ing */
126 list = &obj->map_list;
127 list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
128 if (!list->map)
129 return -ENOMEM;
130
131 map = list->map;
132 map->type = _DRM_GEM;
133 map->size = size;
134 map->handle = obj;
135
136 /* Get a DRM GEM mmap offset allocated... */
137 list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
138 size / PAGE_SIZE, 0, 0);
139
140 if (!list->file_offset_node) {
141 DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
142 ret = -ENOSPC;
143 goto out_free_list;
144 }
145
146 list->file_offset_node = drm_mm_get_block(list->file_offset_node,
147 size / PAGE_SIZE, 0);
148 if (!list->file_offset_node) {
149 ret = -ENOMEM;
150 goto out_free_list;
151 }
152
153 list->hash.key = list->file_offset_node->start;
154 ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
155 if (ret) {
156 DRM_ERROR("failed to add to map hash\n");
157 goto out_free_mm;
158 }
159
160 return 0;
161
162out_free_mm:
163 drm_mm_put_block(list->file_offset_node);
164out_free_list:
165 kfree(list->map);
166 list->map = NULL;
167
168 return ret;
169}
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
new file mode 100644
index 000000000000..e01303ee00c3
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
@@ -0,0 +1,322 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_irq.c
3 *
4 * Copyright (C) 2012 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22static DEFINE_SPINLOCK(list_lock);
23
24static void omap_irq_error_handler(struct omap_drm_irq *irq,
25 uint32_t irqstatus)
26{
27 DRM_ERROR("errors: %08x\n", irqstatus);
28}
29
30/* call with list_lock and dispc runtime held */
31static void omap_irq_update(struct drm_device *dev)
32{
33 struct omap_drm_private *priv = dev->dev_private;
34 struct omap_drm_irq *irq;
35 uint32_t irqmask = priv->vblank_mask;
36
37 BUG_ON(!spin_is_locked(&list_lock));
38
39 list_for_each_entry(irq, &priv->irq_list, node)
40 irqmask |= irq->irqmask;
41
42 DBG("irqmask=%08x", irqmask);
43
44 dispc_write_irqenable(irqmask);
45 dispc_read_irqenable(); /* flush posted write */
46}
47
48void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq)
49{
50 struct omap_drm_private *priv = dev->dev_private;
51 unsigned long flags;
52
53 dispc_runtime_get();
54 spin_lock_irqsave(&list_lock, flags);
55
56 if (!WARN_ON(irq->registered)) {
57 irq->registered = true;
58 list_add(&irq->node, &priv->irq_list);
59 omap_irq_update(dev);
60 }
61
62 spin_unlock_irqrestore(&list_lock, flags);
63 dispc_runtime_put();
64}
65
66void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq)
67{
68 unsigned long flags;
69
70 dispc_runtime_get();
71 spin_lock_irqsave(&list_lock, flags);
72
73 if (!WARN_ON(!irq->registered)) {
74 irq->registered = false;
75 list_del(&irq->node);
76 omap_irq_update(dev);
77 }
78
79 spin_unlock_irqrestore(&list_lock, flags);
80 dispc_runtime_put();
81}
82
83struct omap_irq_wait {
84 struct omap_drm_irq irq;
85 int count;
86};
87
88static DECLARE_WAIT_QUEUE_HEAD(wait_event);
89
90static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
91{
92 struct omap_irq_wait *wait =
93 container_of(irq, struct omap_irq_wait, irq);
94 wait->count--;
95 wake_up_all(&wait_event);
96}
97
98struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
99 uint32_t irqmask, int count)
100{
101 struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
102 wait->irq.irq = wait_irq;
103 wait->irq.irqmask = irqmask;
104 wait->count = count;
105 omap_irq_register(dev, &wait->irq);
106 return wait;
107}
108
109int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
110 unsigned long timeout)
111{
112 int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout);
113 omap_irq_unregister(dev, &wait->irq);
114 kfree(wait);
115 if (ret == 0)
116 return -1;
117 return 0;
118}
119
120/**
121 * enable_vblank - enable vblank interrupt events
122 * @dev: DRM device
123 * @crtc: which irq to enable
124 *
125 * Enable vblank interrupts for @crtc. If the device doesn't have
126 * a hardware vblank counter, this routine should be a no-op, since
127 * interrupts will have to stay on to keep the count accurate.
128 *
129 * RETURNS
130 * Zero on success, appropriate errno if the given @crtc's vblank
131 * interrupt cannot be enabled.
132 */
133int omap_irq_enable_vblank(struct drm_device *dev, int crtc)
134{
135 struct omap_drm_private *priv = dev->dev_private;
136 unsigned long flags;
137
138 DBG("dev=%p, crtc=%d", dev, crtc);
139
140 dispc_runtime_get();
141 spin_lock_irqsave(&list_lock, flags);
142 priv->vblank_mask |= pipe2vbl(crtc);
143 omap_irq_update(dev);
144 spin_unlock_irqrestore(&list_lock, flags);
145 dispc_runtime_put();
146
147 return 0;
148}
149
150/**
151 * disable_vblank - disable vblank interrupt events
152 * @dev: DRM device
153 * @crtc: which irq to enable
154 *
155 * Disable vblank interrupts for @crtc. If the device doesn't have
156 * a hardware vblank counter, this routine should be a no-op, since
157 * interrupts will have to stay on to keep the count accurate.
158 */
159void omap_irq_disable_vblank(struct drm_device *dev, int crtc)
160{
161 struct omap_drm_private *priv = dev->dev_private;
162 unsigned long flags;
163
164 DBG("dev=%p, crtc=%d", dev, crtc);
165
166 dispc_runtime_get();
167 spin_lock_irqsave(&list_lock, flags);
168 priv->vblank_mask &= ~pipe2vbl(crtc);
169 omap_irq_update(dev);
170 spin_unlock_irqrestore(&list_lock, flags);
171 dispc_runtime_put();
172}
173
174irqreturn_t omap_irq_handler(DRM_IRQ_ARGS)
175{
176 struct drm_device *dev = (struct drm_device *) arg;
177 struct omap_drm_private *priv = dev->dev_private;
178 struct omap_drm_irq *handler, *n;
179 unsigned long flags;
180 unsigned int id;
181 u32 irqstatus;
182
183 irqstatus = dispc_read_irqstatus();
184 dispc_clear_irqstatus(irqstatus);
185 dispc_read_irqstatus(); /* flush posted write */
186
187 VERB("irqs: %08x", irqstatus);
188
189 for (id = 0; id < priv->num_crtcs; id++)
190 if (irqstatus & pipe2vbl(id))
191 drm_handle_vblank(dev, id);
192
193 spin_lock_irqsave(&list_lock, flags);
194 list_for_each_entry_safe(handler, n, &priv->irq_list, node) {
195 if (handler->irqmask & irqstatus) {
196 spin_unlock_irqrestore(&list_lock, flags);
197 handler->irq(handler, handler->irqmask & irqstatus);
198 spin_lock_irqsave(&list_lock, flags);
199 }
200 }
201 spin_unlock_irqrestore(&list_lock, flags);
202
203 return IRQ_HANDLED;
204}
205
206void omap_irq_preinstall(struct drm_device *dev)
207{
208 DBG("dev=%p", dev);
209 dispc_runtime_get();
210 dispc_clear_irqstatus(0xffffffff);
211 dispc_runtime_put();
212}
213
214int omap_irq_postinstall(struct drm_device *dev)
215{
216 struct omap_drm_private *priv = dev->dev_private;
217 struct omap_drm_irq *error_handler = &priv->error_handler;
218
219 DBG("dev=%p", dev);
220
221 INIT_LIST_HEAD(&priv->irq_list);
222
223 error_handler->irq = omap_irq_error_handler;
224 error_handler->irqmask = DISPC_IRQ_OCP_ERR;
225
226 /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think
227 * we just need to ignore it while enabling tv-out
228 */
229 error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
230
231 omap_irq_register(dev, error_handler);
232
233 return 0;
234}
235
236void omap_irq_uninstall(struct drm_device *dev)
237{
238 DBG("dev=%p", dev);
239 // TODO prolly need to call drm_irq_uninstall() somewhere too
240}
241
242/*
243 * We need a special version, instead of just using drm_irq_install(),
244 * because we need to register the irq via omapdss. Once omapdss and
245 * omapdrm are merged together we can assign the dispc hwmod data to
246 * ourselves and drop these and just use drm_irq_{install,uninstall}()
247 */
248
249int omap_drm_irq_install(struct drm_device *dev)
250{
251 int ret;
252
253 mutex_lock(&dev->struct_mutex);
254
255 if (dev->irq_enabled) {
256 mutex_unlock(&dev->struct_mutex);
257 return -EBUSY;
258 }
259 dev->irq_enabled = 1;
260 mutex_unlock(&dev->struct_mutex);
261
262 /* Before installing handler */
263 if (dev->driver->irq_preinstall)
264 dev->driver->irq_preinstall(dev);
265
266 ret = dispc_request_irq(dev->driver->irq_handler, dev);
267
268 if (ret < 0) {
269 mutex_lock(&dev->struct_mutex);
270 dev->irq_enabled = 0;
271 mutex_unlock(&dev->struct_mutex);
272 return ret;
273 }
274
275 /* After installing handler */
276 if (dev->driver->irq_postinstall)
277 ret = dev->driver->irq_postinstall(dev);
278
279 if (ret < 0) {
280 mutex_lock(&dev->struct_mutex);
281 dev->irq_enabled = 0;
282 mutex_unlock(&dev->struct_mutex);
283 dispc_free_irq(dev);
284 }
285
286 return ret;
287}
288
289int omap_drm_irq_uninstall(struct drm_device *dev)
290{
291 unsigned long irqflags;
292 int irq_enabled, i;
293
294 mutex_lock(&dev->struct_mutex);
295 irq_enabled = dev->irq_enabled;
296 dev->irq_enabled = 0;
297 mutex_unlock(&dev->struct_mutex);
298
299 /*
300 * Wake up any waiters so they don't hang.
301 */
302 if (dev->num_crtcs) {
303 spin_lock_irqsave(&dev->vbl_lock, irqflags);
304 for (i = 0; i < dev->num_crtcs; i++) {
305 DRM_WAKEUP(&dev->vbl_queue[i]);
306 dev->vblank_enabled[i] = 0;
307 dev->last_vblank[i] =
308 dev->driver->get_vblank_counter(dev, i);
309 }
310 spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
311 }
312
313 if (!irq_enabled)
314 return -EINVAL;
315
316 if (dev->driver->irq_uninstall)
317 dev->driver->irq_uninstall(dev);
318
319 dispc_free_irq(dev);
320
321 return 0;
322}
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
new file mode 100644
index 000000000000..dd68d14ce615
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -0,0 +1,450 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_plane.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/kfifo.h>
21
22#include "omap_drv.h"
23#include "omap_dmm_tiler.h"
24
25/* some hackery because omapdss has an 'enum omap_plane' (which would be
26 * better named omap_plane_id).. and compiler seems unhappy about having
27 * both a 'struct omap_plane' and 'enum omap_plane'
28 */
29#define omap_plane _omap_plane
30
31/*
32 * plane funcs
33 */
34
35struct callback {
36 void (*fxn)(void *);
37 void *arg;
38};
39
40#define to_omap_plane(x) container_of(x, struct omap_plane, base)
41
42struct omap_plane {
43 struct drm_plane base;
44 int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
45 const char *name;
46 struct omap_overlay_info info;
47 struct omap_drm_apply apply;
48
49 /* position/orientation of scanout within the fb: */
50 struct omap_drm_window win;
51 bool enabled;
52
53 /* last fb that we pinned: */
54 struct drm_framebuffer *pinned_fb;
55
56 uint32_t nformats;
57 uint32_t formats[32];
58
59 struct omap_drm_irq error_irq;
60
61 /* set of bo's pending unpin until next post_apply() */
62 DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
63
64 // XXX maybe get rid of this and handle vblank in crtc too?
65 struct callback apply_done_cb;
66};
67
68static void unpin(void *arg, struct drm_gem_object *bo)
69{
70 struct drm_plane *plane = arg;
71 struct omap_plane *omap_plane = to_omap_plane(plane);
72
73 if (kfifo_put(&omap_plane->unpin_fifo,
74 (const struct drm_gem_object **)&bo)) {
75 /* also hold a ref so it isn't free'd while pinned */
76 drm_gem_object_reference(bo);
77 } else {
78 dev_err(plane->dev->dev, "unpin fifo full!\n");
79 omap_gem_put_paddr(bo);
80 }
81}
82
83/* update which fb (if any) is pinned for scanout */
84static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
85{
86 struct omap_plane *omap_plane = to_omap_plane(plane);
87 struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
88
89 if (pinned_fb != fb) {
90 int ret;
91
92 DBG("%p -> %p", pinned_fb, fb);
93
94 if (fb)
95 drm_framebuffer_reference(fb);
96
97 ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
98
99 if (pinned_fb)
100 drm_framebuffer_unreference(pinned_fb);
101
102 if (ret) {
103 dev_err(plane->dev->dev, "could not swap %p -> %p\n",
104 omap_plane->pinned_fb, fb);
105 if (fb)
106 drm_framebuffer_unreference(fb);
107 omap_plane->pinned_fb = NULL;
108 return ret;
109 }
110
111 omap_plane->pinned_fb = fb;
112 }
113
114 return 0;
115}
116
117static void omap_plane_pre_apply(struct omap_drm_apply *apply)
118{
119 struct omap_plane *omap_plane =
120 container_of(apply, struct omap_plane, apply);
121 struct omap_drm_window *win = &omap_plane->win;
122 struct drm_plane *plane = &omap_plane->base;
123 struct drm_device *dev = plane->dev;
124 struct omap_overlay_info *info = &omap_plane->info;
125 struct drm_crtc *crtc = plane->crtc;
126 enum omap_channel channel;
127 bool enabled = omap_plane->enabled && crtc;
128 bool ilace, replication;
129 int ret;
130
131 DBG("%s, enabled=%d", omap_plane->name, enabled);
132
133 /* if fb has changed, pin new fb: */
134 update_pin(plane, enabled ? plane->fb : NULL);
135
136 if (!enabled) {
137 dispc_ovl_enable(omap_plane->id, false);
138 return;
139 }
140
141 channel = omap_crtc_channel(crtc);
142
143 /* update scanout: */
144 omap_framebuffer_update_scanout(plane->fb, win, info);
145
146 DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
147 info->out_width, info->out_height,
148 info->screen_width);
149 DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
150 info->paddr, info->p_uv_addr);
151
152 /* TODO: */
153 ilace = false;
154 replication = false;
155
156 /* and finally, update omapdss: */
157 ret = dispc_ovl_setup(omap_plane->id, info,
158 replication, omap_crtc_timings(crtc), false);
159 if (ret) {
160 dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
161 return;
162 }
163
164 dispc_ovl_enable(omap_plane->id, true);
165 dispc_ovl_set_channel_out(omap_plane->id, channel);
166}
167
168static void omap_plane_post_apply(struct omap_drm_apply *apply)
169{
170 struct omap_plane *omap_plane =
171 container_of(apply, struct omap_plane, apply);
172 struct drm_plane *plane = &omap_plane->base;
173 struct omap_overlay_info *info = &omap_plane->info;
174 struct drm_gem_object *bo = NULL;
175 struct callback cb;
176
177 cb = omap_plane->apply_done_cb;
178 omap_plane->apply_done_cb.fxn = NULL;
179
180 while (kfifo_get(&omap_plane->unpin_fifo, &bo)) {
181 omap_gem_put_paddr(bo);
182 drm_gem_object_unreference_unlocked(bo);
183 }
184
185 if (cb.fxn)
186 cb.fxn(cb.arg);
187
188 if (omap_plane->enabled) {
189 omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
190 info->out_width, info->out_height);
191 }
192}
193
194static int apply(struct drm_plane *plane)
195{
196 if (plane->crtc) {
197 struct omap_plane *omap_plane = to_omap_plane(plane);
198 return omap_crtc_apply(plane->crtc, &omap_plane->apply);
199 }
200 return 0;
201}
202
203int omap_plane_mode_set(struct drm_plane *plane,
204 struct drm_crtc *crtc, struct drm_framebuffer *fb,
205 int crtc_x, int crtc_y,
206 unsigned int crtc_w, unsigned int crtc_h,
207 uint32_t src_x, uint32_t src_y,
208 uint32_t src_w, uint32_t src_h,
209 void (*fxn)(void *), void *arg)
210{
211 struct omap_plane *omap_plane = to_omap_plane(plane);
212 struct omap_drm_window *win = &omap_plane->win;
213
214 win->crtc_x = crtc_x;
215 win->crtc_y = crtc_y;
216 win->crtc_w = crtc_w;
217 win->crtc_h = crtc_h;
218
219 /* src values are in Q16 fixed point, convert to integer: */
220 win->src_x = src_x >> 16;
221 win->src_y = src_y >> 16;
222 win->src_w = src_w >> 16;
223 win->src_h = src_h >> 16;
224
225 if (fxn) {
226 /* omap_crtc should ensure that a new page flip
227 * isn't permitted while there is one pending:
228 */
229 BUG_ON(omap_plane->apply_done_cb.fxn);
230
231 omap_plane->apply_done_cb.fxn = fxn;
232 omap_plane->apply_done_cb.arg = arg;
233 }
234
235 plane->fb = fb;
236 plane->crtc = crtc;
237
238 return apply(plane);
239}
240
241static int omap_plane_update(struct drm_plane *plane,
242 struct drm_crtc *crtc, struct drm_framebuffer *fb,
243 int crtc_x, int crtc_y,
244 unsigned int crtc_w, unsigned int crtc_h,
245 uint32_t src_x, uint32_t src_y,
246 uint32_t src_w, uint32_t src_h)
247{
248 struct omap_plane *omap_plane = to_omap_plane(plane);
249 omap_plane->enabled = true;
250 return omap_plane_mode_set(plane, crtc, fb,
251 crtc_x, crtc_y, crtc_w, crtc_h,
252 src_x, src_y, src_w, src_h,
253 NULL, NULL);
254}
255
256static int omap_plane_disable(struct drm_plane *plane)
257{
258 struct omap_plane *omap_plane = to_omap_plane(plane);
259 omap_plane->win.rotation = BIT(DRM_ROTATE_0);
260 return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
261}
262
263static void omap_plane_destroy(struct drm_plane *plane)
264{
265 struct omap_plane *omap_plane = to_omap_plane(plane);
266
267 DBG("%s", omap_plane->name);
268
269 omap_irq_unregister(plane->dev, &omap_plane->error_irq);
270
271 omap_plane_disable(plane);
272 drm_plane_cleanup(plane);
273
274 WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo));
275 kfifo_free(&omap_plane->unpin_fifo);
276
277 kfree(omap_plane);
278}
279
280int omap_plane_dpms(struct drm_plane *plane, int mode)
281{
282 struct omap_plane *omap_plane = to_omap_plane(plane);
283 bool enabled = (mode == DRM_MODE_DPMS_ON);
284 int ret = 0;
285
286 if (enabled != omap_plane->enabled) {
287 omap_plane->enabled = enabled;
288 ret = apply(plane);
289 }
290
291 return ret;
292}
293
294/* helper to install properties which are common to planes and crtcs */
295void omap_plane_install_properties(struct drm_plane *plane,
296 struct drm_mode_object *obj)
297{
298 struct drm_device *dev = plane->dev;
299 struct omap_drm_private *priv = dev->dev_private;
300 struct drm_property *prop;
301
302 if (priv->has_dmm) {
303 prop = priv->rotation_prop;
304 if (!prop) {
305 const struct drm_prop_enum_list props[] = {
306 { DRM_ROTATE_0, "rotate-0" },
307 { DRM_ROTATE_90, "rotate-90" },
308 { DRM_ROTATE_180, "rotate-180" },
309 { DRM_ROTATE_270, "rotate-270" },
310 { DRM_REFLECT_X, "reflect-x" },
311 { DRM_REFLECT_Y, "reflect-y" },
312 };
313 prop = drm_property_create_bitmask(dev, 0, "rotation",
314 props, ARRAY_SIZE(props));
315 if (prop == NULL)
316 return;
317 priv->rotation_prop = prop;
318 }
319 drm_object_attach_property(obj, prop, 0);
320 }
321
322 prop = priv->zorder_prop;
323 if (!prop) {
324 prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
325 if (prop == NULL)
326 return;
327 priv->zorder_prop = prop;
328 }
329 drm_object_attach_property(obj, prop, 0);
330}
331
332int omap_plane_set_property(struct drm_plane *plane,
333 struct drm_property *property, uint64_t val)
334{
335 struct omap_plane *omap_plane = to_omap_plane(plane);
336 struct omap_drm_private *priv = plane->dev->dev_private;
337 int ret = -EINVAL;
338
339 if (property == priv->rotation_prop) {
340 DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
341 omap_plane->win.rotation = val;
342 ret = apply(plane);
343 } else if (property == priv->zorder_prop) {
344 DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
345 omap_plane->info.zorder = val;
346 ret = apply(plane);
347 }
348
349 return ret;
350}
351
352static const struct drm_plane_funcs omap_plane_funcs = {
353 .update_plane = omap_plane_update,
354 .disable_plane = omap_plane_disable,
355 .destroy = omap_plane_destroy,
356 .set_property = omap_plane_set_property,
357};
358
359static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
360{
361 struct omap_plane *omap_plane =
362 container_of(irq, struct omap_plane, error_irq);
363 DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
364}
365
366static const char *plane_names[] = {
367 [OMAP_DSS_GFX] = "gfx",
368 [OMAP_DSS_VIDEO1] = "vid1",
369 [OMAP_DSS_VIDEO2] = "vid2",
370 [OMAP_DSS_VIDEO3] = "vid3",
371};
372
373static const uint32_t error_irqs[] = {
374 [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
375 [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
376 [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
377 [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
378};
379
380/* initialize plane */
381struct drm_plane *omap_plane_init(struct drm_device *dev,
382 int id, bool private_plane)
383{
384 struct omap_drm_private *priv = dev->dev_private;
385 struct drm_plane *plane = NULL;
386 struct omap_plane *omap_plane;
387 struct omap_overlay_info *info;
388 int ret;
389
390 DBG("%s: priv=%d", plane_names[id], private_plane);
391
392 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
393 if (!omap_plane) {
394 dev_err(dev->dev, "could not allocate plane\n");
395 goto fail;
396 }
397
398 ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
399 if (ret) {
400 dev_err(dev->dev, "could not allocate unpin FIFO\n");
401 goto fail;
402 }
403
404 omap_plane->nformats = omap_framebuffer_get_formats(
405 omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
406 dss_feat_get_supported_color_modes(id));
407 omap_plane->id = id;
408 omap_plane->name = plane_names[id];
409
410 plane = &omap_plane->base;
411
412 omap_plane->apply.pre_apply = omap_plane_pre_apply;
413 omap_plane->apply.post_apply = omap_plane_post_apply;
414
415 omap_plane->error_irq.irqmask = error_irqs[id];
416 omap_plane->error_irq.irq = omap_plane_error_irq;
417 omap_irq_register(dev, &omap_plane->error_irq);
418
419 drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
420 omap_plane->formats, omap_plane->nformats, private_plane);
421
422 omap_plane_install_properties(plane, &plane->base);
423
424 /* get our starting configuration, set defaults for parameters
425 * we don't currently use, etc:
426 */
427 info = &omap_plane->info;
428 info->rotation_type = OMAP_DSS_ROT_DMA;
429 info->rotation = OMAP_DSS_ROT_0;
430 info->global_alpha = 0xff;
431 info->mirror = 0;
432
433 /* Set defaults depending on whether we are a CRTC or overlay
434 * layer.
435 * TODO add ioctl to give userspace an API to change this.. this
436 * will come in a subsequent patch.
437 */
438 if (private_plane)
439 omap_plane->info.zorder = 0;
440 else
441 omap_plane->info.zorder = id;
442
443 return plane;
444
445fail:
446 if (plane)
447 omap_plane_destroy(plane);
448
449 return NULL;
450}
diff --git a/drivers/gpu/drm/omapdrm/tcm-sita.c b/drivers/gpu/drm/omapdrm/tcm-sita.c
new file mode 100644
index 000000000000..efb609510540
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/tcm-sita.c
@@ -0,0 +1,703 @@
1/*
2 * tcm-sita.c
3 *
4 * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
5 *
6 * Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
7 * Lajos Molnar <molnar@ti.com>
8 *
9 * Copyright (C) 2009-2010 Texas Instruments, Inc.
10 *
11 * This package is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 */
20#include <linux/slab.h>
21#include <linux/spinlock.h>
22
23#include "tcm-sita.h"
24
25#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1))
26
27/* Individual selection criteria for different scan areas */
28static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
29static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
30
31/*********************************************
32 * TCM API - Sita Implementation
33 *********************************************/
34static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
35 struct tcm_area *area);
36static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
37static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
38static void sita_deinit(struct tcm *tcm);
39
40/*********************************************
41 * Main Scanner functions
42 *********************************************/
43static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
44 struct tcm_area *area);
45
46static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
47 struct tcm_area *field, struct tcm_area *area);
48
49static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
50 struct tcm_area *field, struct tcm_area *area);
51
52static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
53 struct tcm_area *field, struct tcm_area *area);
54
55/*********************************************
56 * Support Infrastructure Methods
57 *********************************************/
58static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
59
60static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
61 struct tcm_area *field, s32 criteria,
62 struct score *best);
63
64static void get_nearness_factor(struct tcm_area *field,
65 struct tcm_area *candidate,
66 struct nearness_factor *nf);
67
68static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
69 struct neighbor_stats *stat);
70
71static void fill_area(struct tcm *tcm,
72 struct tcm_area *area, struct tcm_area *parent);
73
74
75/*********************************************/
76
77/*********************************************
78 * Utility Methods
79 *********************************************/
80struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
81{
82 struct tcm *tcm;
83 struct sita_pvt *pvt;
84 struct tcm_area area = {0};
85 s32 i;
86
87 if (width == 0 || height == 0)
88 return NULL;
89
90 tcm = kmalloc(sizeof(*tcm), GFP_KERNEL);
91 pvt = kmalloc(sizeof(*pvt), GFP_KERNEL);
92 if (!tcm || !pvt)
93 goto error;
94
95 memset(tcm, 0, sizeof(*tcm));
96 memset(pvt, 0, sizeof(*pvt));
97
98 /* Updating the pointers to SiTA implementation APIs */
99 tcm->height = height;
100 tcm->width = width;
101 tcm->reserve_2d = sita_reserve_2d;
102 tcm->reserve_1d = sita_reserve_1d;
103 tcm->free = sita_free;
104 tcm->deinit = sita_deinit;
105 tcm->pvt = (void *)pvt;
106
107 spin_lock_init(&(pvt->lock));
108
109 /* Creating tam map */
110 pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL);
111 if (!pvt->map)
112 goto error;
113
114 for (i = 0; i < tcm->width; i++) {
115 pvt->map[i] =
116 kmalloc(sizeof(**pvt->map) * tcm->height,
117 GFP_KERNEL);
118 if (pvt->map[i] == NULL) {
119 while (i--)
120 kfree(pvt->map[i]);
121 kfree(pvt->map);
122 goto error;
123 }
124 }
125
126 if (attr && attr->x <= tcm->width && attr->y <= tcm->height) {
127 pvt->div_pt.x = attr->x;
128 pvt->div_pt.y = attr->y;
129
130 } else {
131 /* Defaulting to 3:1 ratio on width for 2D area split */
132 /* Defaulting to 3:1 ratio on height for 2D and 1D split */
133 pvt->div_pt.x = (tcm->width * 3) / 4;
134 pvt->div_pt.y = (tcm->height * 3) / 4;
135 }
136
137 spin_lock(&(pvt->lock));
138 assign(&area, 0, 0, width - 1, height - 1);
139 fill_area(tcm, &area, NULL);
140 spin_unlock(&(pvt->lock));
141 return tcm;
142
143error:
144 kfree(tcm);
145 kfree(pvt);
146 return NULL;
147}
148
149static void sita_deinit(struct tcm *tcm)
150{
151 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
152 struct tcm_area area = {0};
153 s32 i;
154
155 area.p1.x = tcm->width - 1;
156 area.p1.y = tcm->height - 1;
157
158 spin_lock(&(pvt->lock));
159 fill_area(tcm, &area, NULL);
160 spin_unlock(&(pvt->lock));
161
162 for (i = 0; i < tcm->height; i++)
163 kfree(pvt->map[i]);
164 kfree(pvt->map);
165 kfree(pvt);
166}
167
168/**
169 * Reserve a 1D area in the container
170 *
171 * @param num_slots size of 1D area
172 * @param area pointer to the area that will be populated with the
173 * reserved area
174 *
175 * @return 0 on success, non-0 error value on failure.
176 */
177static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
178 struct tcm_area *area)
179{
180 s32 ret;
181 struct tcm_area field = {0};
182 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
183
184 spin_lock(&(pvt->lock));
185
186 /* Scanning entire container */
187 assign(&field, tcm->width - 1, tcm->height - 1, 0, 0);
188
189 ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area);
190 if (!ret)
191 /* update map */
192 fill_area(tcm, area, area);
193
194 spin_unlock(&(pvt->lock));
195 return ret;
196}
197
198/**
199 * Reserve a 2D area in the container
200 *
201 * @param w width
202 * @param h height
203 * @param area pointer to the area that will be populated with the reserved
204 * area
205 *
206 * @return 0 on success, non-0 error value on failure.
207 */
208static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
209 struct tcm_area *area)
210{
211 s32 ret;
212 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
213
214 /* not supporting more than 64 as alignment */
215 if (align > 64)
216 return -EINVAL;
217
218 /* we prefer 1, 32 and 64 as alignment */
219 align = align <= 1 ? 1 : align <= 32 ? 32 : 64;
220
221 spin_lock(&(pvt->lock));
222 ret = scan_areas_and_find_fit(tcm, w, h, align, area);
223 if (!ret)
224 /* update map */
225 fill_area(tcm, area, area);
226
227 spin_unlock(&(pvt->lock));
228 return ret;
229}
230
231/**
232 * Unreserve a previously allocated 2D or 1D area
233 * @param area area to be freed
234 * @return 0 - success
235 */
236static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
237{
238 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
239
240 spin_lock(&(pvt->lock));
241
242 /* check that this is in fact an existing area */
243 WARN_ON(pvt->map[area->p0.x][area->p0.y] != area ||
244 pvt->map[area->p1.x][area->p1.y] != area);
245
246 /* Clear the contents of the associated tiles in the map */
247 fill_area(tcm, area, NULL);
248
249 spin_unlock(&(pvt->lock));
250
251 return 0;
252}
253
254/**
255 * Note: In general the cordinates in the scan field area relevant to the can
256 * sweep directions. The scan origin (e.g. top-left corner) will always be
257 * the p0 member of the field. Therfore, for a scan from top-left p0.x <= p1.x
258 * and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y
259 * <= p0.y
260 */
261
262/**
263 * Raster scan horizontally right to left from top to bottom to find a place for
264 * a 2D area of given size inside a scan field.
265 *
266 * @param w width of desired area
267 * @param h height of desired area
268 * @param align desired area alignment
269 * @param area pointer to the area that will be set to the best position
270 * @param field area to scan (inclusive)
271 *
272 * @return 0 on success, non-0 error value on failure.
273 */
274static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
275 struct tcm_area *field, struct tcm_area *area)
276{
277 s32 x, y;
278 s16 start_x, end_x, start_y, end_y, found_x = -1;
279 struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
280 struct score best = {{0}, {0}, {0}, 0};
281
282 start_x = field->p0.x;
283 end_x = field->p1.x;
284 start_y = field->p0.y;
285 end_y = field->p1.y;
286
287 /* check scan area co-ordinates */
288 if (field->p0.x < field->p1.x ||
289 field->p1.y < field->p0.y)
290 return -EINVAL;
291
292 /* check if allocation would fit in scan area */
293 if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y))
294 return -ENOSPC;
295
296 /* adjust start_x and end_y, as allocation would not fit beyond */
297 start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */
298 end_y = end_y - h + 1;
299
300 /* check if allocation would still fit in scan area */
301 if (start_x < end_x)
302 return -ENOSPC;
303
304 /* scan field top-to-bottom, right-to-left */
305 for (y = start_y; y <= end_y; y++) {
306 for (x = start_x; x >= end_x; x -= align) {
307 if (is_area_free(map, x, y, w, h)) {
308 found_x = x;
309
310 /* update best candidate */
311 if (update_candidate(tcm, x, y, w, h, field,
312 CR_R2L_T2B, &best))
313 goto done;
314
315 /* change upper x bound */
316 end_x = x + 1;
317 break;
318 } else if (map[x][y] && map[x][y]->is2d) {
319 /* step over 2D areas */
320 x = ALIGN(map[x][y]->p0.x - w + 1, align);
321 }
322 }
323
324 /* break if you find a free area shouldering the scan field */
325 if (found_x == start_x)
326 break;
327 }
328
329 if (!best.a.tcm)
330 return -ENOSPC;
331done:
332 assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
333 return 0;
334}
335
336/**
337 * Raster scan horizontally left to right from top to bottom to find a place for
338 * a 2D area of given size inside a scan field.
339 *
340 * @param w width of desired area
341 * @param h height of desired area
342 * @param align desired area alignment
343 * @param area pointer to the area that will be set to the best position
344 * @param field area to scan (inclusive)
345 *
346 * @return 0 on success, non-0 error value on failure.
347 */
348static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
349 struct tcm_area *field, struct tcm_area *area)
350{
351 s32 x, y;
352 s16 start_x, end_x, start_y, end_y, found_x = -1;
353 struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
354 struct score best = {{0}, {0}, {0}, 0};
355
356 start_x = field->p0.x;
357 end_x = field->p1.x;
358 start_y = field->p0.y;
359 end_y = field->p1.y;
360
361 /* check scan area co-ordinates */
362 if (field->p1.x < field->p0.x ||
363 field->p1.y < field->p0.y)
364 return -EINVAL;
365
366 /* check if allocation would fit in scan area */
367 if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y))
368 return -ENOSPC;
369
370 start_x = ALIGN(start_x, align);
371
372 /* check if allocation would still fit in scan area */
373 if (w > LEN(end_x, start_x))
374 return -ENOSPC;
375
376 /* adjust end_x and end_y, as allocation would not fit beyond */
377 end_x = end_x - w + 1; /* + 1 to be inclusive */
378 end_y = end_y - h + 1;
379
380 /* scan field top-to-bottom, left-to-right */
381 for (y = start_y; y <= end_y; y++) {
382 for (x = start_x; x <= end_x; x += align) {
383 if (is_area_free(map, x, y, w, h)) {
384 found_x = x;
385
386 /* update best candidate */
387 if (update_candidate(tcm, x, y, w, h, field,
388 CR_L2R_T2B, &best))
389 goto done;
390 /* change upper x bound */
391 end_x = x - 1;
392
393 break;
394 } else if (map[x][y] && map[x][y]->is2d) {
395 /* step over 2D areas */
396 x = ALIGN_DOWN(map[x][y]->p1.x, align);
397 }
398 }
399
400 /* break if you find a free area shouldering the scan field */
401 if (found_x == start_x)
402 break;
403 }
404
405 if (!best.a.tcm)
406 return -ENOSPC;
407done:
408 assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
409 return 0;
410}
411
412/**
413 * Raster scan horizontally right to left from bottom to top to find a place
414 * for a 1D area of given size inside a scan field.
415 *
416 * @param num_slots size of desired area
417 * @param align desired area alignment
418 * @param area pointer to the area that will be set to the best
419 * position
420 * @param field area to scan (inclusive)
421 *
422 * @return 0 on success, non-0 error value on failure.
423 */
424static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
425 struct tcm_area *field, struct tcm_area *area)
426{
427 s32 found = 0;
428 s16 x, y;
429 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
430 struct tcm_area *p;
431
432 /* check scan area co-ordinates */
433 if (field->p0.y < field->p1.y)
434 return -EINVAL;
435
436 /**
437 * Currently we only support full width 1D scan field, which makes sense
438 * since 1D slot-ordering spans the full container width.
439 */
440 if (tcm->width != field->p0.x - field->p1.x + 1)
441 return -EINVAL;
442
443 /* check if allocation would fit in scan area */
444 if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y))
445 return -ENOSPC;
446
447 x = field->p0.x;
448 y = field->p0.y;
449
450 /* find num_slots consecutive free slots to the left */
451 while (found < num_slots) {
452 if (y < 0)
453 return -ENOSPC;
454
455 /* remember bottom-right corner */
456 if (found == 0) {
457 area->p1.x = x;
458 area->p1.y = y;
459 }
460
461 /* skip busy regions */
462 p = pvt->map[x][y];
463 if (p) {
464 /* move to left of 2D areas, top left of 1D */
465 x = p->p0.x;
466 if (!p->is2d)
467 y = p->p0.y;
468
469 /* start over */
470 found = 0;
471 } else {
472 /* count consecutive free slots */
473 found++;
474 if (found == num_slots)
475 break;
476 }
477
478 /* move to the left */
479 if (x == 0)
480 y--;
481 x = (x ? : tcm->width) - 1;
482
483 }
484
485 /* set top-left corner */
486 area->p0.x = x;
487 area->p0.y = y;
488 return 0;
489}
490
491/**
492 * Find a place for a 2D area of given size inside a scan field based on its
493 * alignment needs.
494 *
495 * @param w width of desired area
496 * @param h height of desired area
497 * @param align desired area alignment
498 * @param area pointer to the area that will be set to the best position
499 *
500 * @return 0 on success, non-0 error value on failure.
501 */
502static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
503 struct tcm_area *area)
504{
505 s32 ret = 0;
506 struct tcm_area field = {0};
507 u16 boundary_x, boundary_y;
508 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
509
510 if (align > 1) {
511 /* prefer top-left corner */
512 boundary_x = pvt->div_pt.x - 1;
513 boundary_y = pvt->div_pt.y - 1;
514
515 /* expand width and height if needed */
516 if (w > pvt->div_pt.x)
517 boundary_x = tcm->width - 1;
518 if (h > pvt->div_pt.y)
519 boundary_y = tcm->height - 1;
520
521 assign(&field, 0, 0, boundary_x, boundary_y);
522 ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
523
524 /* scan whole container if failed, but do not scan 2x */
525 if (ret != 0 && (boundary_x != tcm->width - 1 ||
526 boundary_y != tcm->height - 1)) {
527 /* scan the entire container if nothing found */
528 assign(&field, 0, 0, tcm->width - 1, tcm->height - 1);
529 ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
530 }
531 } else if (align == 1) {
532 /* prefer top-right corner */
533 boundary_x = pvt->div_pt.x;
534 boundary_y = pvt->div_pt.y - 1;
535
536 /* expand width and height if needed */
537 if (w > (tcm->width - pvt->div_pt.x))
538 boundary_x = 0;
539 if (h > pvt->div_pt.y)
540 boundary_y = tcm->height - 1;
541
542 assign(&field, tcm->width - 1, 0, boundary_x, boundary_y);
543 ret = scan_r2l_t2b(tcm, w, h, align, &field, area);
544
545 /* scan whole container if failed, but do not scan 2x */
546 if (ret != 0 && (boundary_x != 0 ||
547 boundary_y != tcm->height - 1)) {
548 /* scan the entire container if nothing found */
549 assign(&field, tcm->width - 1, 0, 0, tcm->height - 1);
550 ret = scan_r2l_t2b(tcm, w, h, align, &field,
551 area);
552 }
553 }
554
555 return ret;
556}
557
558/* check if an entire area is free */
559static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h)
560{
561 u16 x = 0, y = 0;
562 for (y = y0; y < y0 + h; y++) {
563 for (x = x0; x < x0 + w; x++) {
564 if (map[x][y])
565 return false;
566 }
567 }
568 return true;
569}
570
571/* fills an area with a parent tcm_area */
572static void fill_area(struct tcm *tcm, struct tcm_area *area,
573 struct tcm_area *parent)
574{
575 s32 x, y;
576 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
577 struct tcm_area a, a_;
578
579 /* set area's tcm; otherwise, enumerator considers it invalid */
580 area->tcm = tcm;
581
582 tcm_for_each_slice(a, *area, a_) {
583 for (x = a.p0.x; x <= a.p1.x; ++x)
584 for (y = a.p0.y; y <= a.p1.y; ++y)
585 pvt->map[x][y] = parent;
586
587 }
588}
589
590/**
591 * Compares a candidate area to the current best area, and if it is a better
592 * fit, it updates the best to this one.
593 *
594 * @param x0, y0, w, h top, left, width, height of candidate area
595 * @param field scan field
596 * @param criteria scan criteria
597 * @param best best candidate and its scores
598 *
599 * @return 1 (true) if the candidate area is known to be the final best, so no
600 * more searching should be performed
601 */
602static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
603 struct tcm_area *field, s32 criteria,
604 struct score *best)
605{
606 struct score me; /* score for area */
607
608 /*
609 * NOTE: For horizontal bias we always give the first found, because our
610 * scan is horizontal-raster-based and the first candidate will always
611 * have the horizontal bias.
612 */
613 bool first = criteria & CR_BIAS_HORIZONTAL;
614
615 assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1);
616
617 /* calculate score for current candidate */
618 if (!first) {
619 get_neighbor_stats(tcm, &me.a, &me.n);
620 me.neighs = me.n.edge + me.n.busy;
621 get_nearness_factor(field, &me.a, &me.f);
622 }
623
624 /* the 1st candidate is always the best */
625 if (!best->a.tcm)
626 goto better;
627
628 BUG_ON(first);
629
630 /* diagonal balance check */
631 if ((criteria & CR_DIAGONAL_BALANCE) &&
632 best->neighs <= me.neighs &&
633 (best->neighs < me.neighs ||
634 /* this implies that neighs and occupied match */
635 best->n.busy < me.n.busy ||
636 (best->n.busy == me.n.busy &&
637 /* check the nearness factor */
638 best->f.x + best->f.y > me.f.x + me.f.y)))
639 goto better;
640
641 /* not better, keep going */
642 return 0;
643
644better:
645 /* save current area as best */
646 memcpy(best, &me, sizeof(me));
647 best->a.tcm = tcm;
648 return first;
649}
650
651/**
652 * Calculate the nearness factor of an area in a search field. The nearness
653 * factor is smaller if the area is closer to the search origin.
654 */
655static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
656 struct nearness_factor *nf)
657{
658 /**
659 * Using signed math as field coordinates may be reversed if
660 * search direction is right-to-left or bottom-to-top.
661 */
662 nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
663 (field->p1.x - field->p0.x);
664 nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
665 (field->p1.y - field->p0.y);
666}
667
668/* get neighbor statistics */
669static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
670 struct neighbor_stats *stat)
671{
672 s16 x = 0, y = 0;
673 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
674
675 /* Clearing any exisiting values */
676 memset(stat, 0, sizeof(*stat));
677
678 /* process top & bottom edges */
679 for (x = area->p0.x; x <= area->p1.x; x++) {
680 if (area->p0.y == 0)
681 stat->edge++;
682 else if (pvt->map[x][area->p0.y - 1])
683 stat->busy++;
684
685 if (area->p1.y == tcm->height - 1)
686 stat->edge++;
687 else if (pvt->map[x][area->p1.y + 1])
688 stat->busy++;
689 }
690
691 /* process left & right edges */
692 for (y = area->p0.y; y <= area->p1.y; ++y) {
693 if (area->p0.x == 0)
694 stat->edge++;
695 else if (pvt->map[area->p0.x - 1][y])
696 stat->busy++;
697
698 if (area->p1.x == tcm->width - 1)
699 stat->edge++;
700 else if (pvt->map[area->p1.x + 1][y])
701 stat->busy++;
702 }
703}
diff --git a/drivers/gpu/drm/omapdrm/tcm-sita.h b/drivers/gpu/drm/omapdrm/tcm-sita.h
new file mode 100644
index 000000000000..0444f868671c
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/tcm-sita.h
@@ -0,0 +1,95 @@
1/*
2 * tcm_sita.h
3 *
4 * SImple Tiler Allocator (SiTA) private structures.
5 *
6 * Author: Ravi Ramachandra <r.ramachandra@ti.com>
7 *
8 * Copyright (C) 2009-2011 Texas Instruments, Inc.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * * Neither the name of Texas Instruments Incorporated nor the names of
23 * its contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
28 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#ifndef _TCM_SITA_H
40#define _TCM_SITA_H
41
42#include "tcm.h"
43
44/* length between two coordinates */
45#define LEN(a, b) ((a) > (b) ? (a) - (b) + 1 : (b) - (a) + 1)
46
47enum criteria {
48 CR_MAX_NEIGHS = 0x01,
49 CR_FIRST_FOUND = 0x10,
50 CR_BIAS_HORIZONTAL = 0x20,
51 CR_BIAS_VERTICAL = 0x40,
52 CR_DIAGONAL_BALANCE = 0x80
53};
54
55/* nearness to the beginning of the search field from 0 to 1000 */
56struct nearness_factor {
57 s32 x;
58 s32 y;
59};
60
61/*
62 * Statistics on immediately neighboring slots. Edge is the number of
63 * border segments that are also border segments of the scan field. Busy
64 * refers to the number of neighbors that are occupied.
65 */
66struct neighbor_stats {
67 u16 edge;
68 u16 busy;
69};
70
71/* structure to keep the score of a potential allocation */
72struct score {
73 struct nearness_factor f;
74 struct neighbor_stats n;
75 struct tcm_area a;
76 u16 neighs; /* number of busy neighbors */
77};
78
79struct sita_pvt {
80 spinlock_t lock; /* spinlock to protect access */
81 struct tcm_pt div_pt; /* divider point splitting container */
82 struct tcm_area ***map; /* pointers to the parent area for each slot */
83};
84
85/* assign coordinates to area */
86static inline
87void assign(struct tcm_area *a, u16 x0, u16 y0, u16 x1, u16 y1)
88{
89 a->p0.x = x0;
90 a->p0.y = y0;
91 a->p1.x = x1;
92 a->p1.y = y1;
93}
94
95#endif
diff --git a/drivers/gpu/drm/omapdrm/tcm.h b/drivers/gpu/drm/omapdrm/tcm.h
new file mode 100644
index 000000000000..a8d5ce47686f
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/tcm.h
@@ -0,0 +1,328 @@
1/*
2 * tcm.h
3 *
4 * TILER container manager specification and support functions for TI
5 * TILER driver.
6 *
7 * Author: Lajos Molnar <molnar@ti.com>
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * * Neither the name of Texas Instruments Incorporated nor the names of
23 * its contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
28 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#ifndef TCM_H
40#define TCM_H
41
42struct tcm;
43
44/* point */
45struct tcm_pt {
46 u16 x;
47 u16 y;
48};
49
50/* 1d or 2d area */
51struct tcm_area {
52 bool is2d; /* whether area is 1d or 2d */
53 struct tcm *tcm; /* parent */
54 struct tcm_pt p0;
55 struct tcm_pt p1;
56};
57
58struct tcm {
59 u16 width, height; /* container dimensions */
60 int lut_id; /* Lookup table identifier */
61
62 unsigned int y_offset; /* offset to use for y coordinates */
63
64 /* 'pvt' structure shall contain any tcm details (attr) along with
65 linked list of allocated areas and mutex for mutually exclusive access
66 to the list. It may also contain copies of width and height to notice
67 any changes to the publicly available width and height fields. */
68 void *pvt;
69
70 /* function table */
71 s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u8 align,
72 struct tcm_area *area);
73 s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area);
74 s32 (*free) (struct tcm *tcm, struct tcm_area *area);
75 void (*deinit) (struct tcm *tcm);
76};
77
78/*=============================================================================
79 BASIC TILER CONTAINER MANAGER INTERFACE
80=============================================================================*/
81
82/*
83 * NOTE:
84 *
85 * Since some basic parameter checking is done outside the TCM algorithms,
86 * TCM implementation do NOT have to check the following:
87 *
88 * area pointer is NULL
89 * width and height fits within container
90 * number of pages is more than the size of the container
91 *
92 */
93
94struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr);
95
96
97/**
98 * Deinitialize tiler container manager.
99 *
100 * @param tcm Pointer to container manager.
101 *
102 * @return 0 on success, non-0 error value on error. The call
103 * should free as much memory as possible and meaningful
104 * even on failure. Some error codes: -ENODEV: invalid
105 * manager.
106 */
107static inline void tcm_deinit(struct tcm *tcm)
108{
109 if (tcm)
110 tcm->deinit(tcm);
111}
112
113/**
114 * Reserves a 2D area in the container.
115 *
116 * @param tcm Pointer to container manager.
117 * @param height Height(in pages) of area to be reserved.
118 * @param width Width(in pages) of area to be reserved.
119 * @param align Alignment requirement for top-left corner of area. Not
120 * all values may be supported by the container manager,
121 * but it must support 0 (1), 32 and 64.
122 * 0 value is equivalent to 1.
123 * @param area Pointer to where the reserved area should be stored.
124 *
125 * @return 0 on success. Non-0 error code on failure. Also,
126 * the tcm field of the area will be set to NULL on
127 * failure. Some error codes: -ENODEV: invalid manager,
128 * -EINVAL: invalid area, -ENOMEM: not enough space for
129 * allocation.
130 */
131static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
132 u16 align, struct tcm_area *area)
133{
134 /* perform rudimentary error checking */
135 s32 res = tcm == NULL ? -ENODEV :
136 (area == NULL || width == 0 || height == 0 ||
137 /* align must be a 2 power */
138 (align & (align - 1))) ? -EINVAL :
139 (height > tcm->height || width > tcm->width) ? -ENOMEM : 0;
140
141 if (!res) {
142 area->is2d = true;
143 res = tcm->reserve_2d(tcm, height, width, align, area);
144 area->tcm = res ? NULL : tcm;
145 }
146
147 return res;
148}
149
150/**
151 * Reserves a 1D area in the container.
152 *
153 * @param tcm Pointer to container manager.
154 * @param slots Number of (contiguous) slots to reserve.
155 * @param area Pointer to where the reserved area should be stored.
156 *
157 * @return 0 on success. Non-0 error code on failure. Also,
158 * the tcm field of the area will be set to NULL on
159 * failure. Some error codes: -ENODEV: invalid manager,
160 * -EINVAL: invalid area, -ENOMEM: not enough space for
161 * allocation.
162 */
163static inline s32 tcm_reserve_1d(struct tcm *tcm, u32 slots,
164 struct tcm_area *area)
165{
166 /* perform rudimentary error checking */
167 s32 res = tcm == NULL ? -ENODEV :
168 (area == NULL || slots == 0) ? -EINVAL :
169 slots > (tcm->width * (u32) tcm->height) ? -ENOMEM : 0;
170
171 if (!res) {
172 area->is2d = false;
173 res = tcm->reserve_1d(tcm, slots, area);
174 area->tcm = res ? NULL : tcm;
175 }
176
177 return res;
178}
179
180/**
181 * Free a previously reserved area from the container.
182 *
183 * @param area Pointer to area reserved by a prior call to
184 * tcm_reserve_1d or tcm_reserve_2d call, whether
185 * it was successful or not. (Note: all fields of
186 * the structure must match.)
187 *
188 * @return 0 on success. Non-0 error code on failure. Also, the tcm
189 * field of the area is set to NULL on success to avoid subsequent
190 * freeing. This call will succeed even if supplying
191 * the area from a failed reserved call.
192 */
193static inline s32 tcm_free(struct tcm_area *area)
194{
195 s32 res = 0; /* free succeeds by default */
196
197 if (area && area->tcm) {
198 res = area->tcm->free(area->tcm, area);
199 if (res == 0)
200 area->tcm = NULL;
201 }
202
203 return res;
204}
205
206/*=============================================================================
207 HELPER FUNCTION FOR ANY TILER CONTAINER MANAGER
208=============================================================================*/
209
210/**
211 * This method slices off the topmost 2D slice from the parent area, and stores
212 * it in the 'slice' parameter. The 'parent' parameter will get modified to
213 * contain the remaining portion of the area. If the whole parent area can
214 * fit in a 2D slice, its tcm pointer is set to NULL to mark that it is no
215 * longer a valid area.
216 *
217 * @param parent Pointer to a VALID parent area that will get modified
218 * @param slice Pointer to the slice area that will get modified
219 */
220static inline void tcm_slice(struct tcm_area *parent, struct tcm_area *slice)
221{
222 *slice = *parent;
223
224 /* check if we need to slice */
225 if (slice->tcm && !slice->is2d &&
226 slice->p0.y != slice->p1.y &&
227 (slice->p0.x || (slice->p1.x != slice->tcm->width - 1))) {
228 /* set end point of slice (start always remains) */
229 slice->p1.x = slice->tcm->width - 1;
230 slice->p1.y = (slice->p0.x) ? slice->p0.y : slice->p1.y - 1;
231 /* adjust remaining area */
232 parent->p0.x = 0;
233 parent->p0.y = slice->p1.y + 1;
234 } else {
235 /* mark this as the last slice */
236 parent->tcm = NULL;
237 }
238}
239
240/* Verify if a tcm area is logically valid */
241static inline bool tcm_area_is_valid(struct tcm_area *area)
242{
243 return area && area->tcm &&
244 /* coordinate bounds */
245 area->p1.x < area->tcm->width &&
246 area->p1.y < area->tcm->height &&
247 area->p0.y <= area->p1.y &&
248 /* 1D coordinate relationship + p0.x check */
249 ((!area->is2d &&
250 area->p0.x < area->tcm->width &&
251 area->p0.x + area->p0.y * area->tcm->width <=
252 area->p1.x + area->p1.y * area->tcm->width) ||
253 /* 2D coordinate relationship */
254 (area->is2d &&
255 area->p0.x <= area->p1.x));
256}
257
258/* see if a coordinate is within an area */
259static inline bool __tcm_is_in(struct tcm_pt *p, struct tcm_area *a)
260{
261 u16 i;
262
263 if (a->is2d) {
264 return p->x >= a->p0.x && p->x <= a->p1.x &&
265 p->y >= a->p0.y && p->y <= a->p1.y;
266 } else {
267 i = p->x + p->y * a->tcm->width;
268 return i >= a->p0.x + a->p0.y * a->tcm->width &&
269 i <= a->p1.x + a->p1.y * a->tcm->width;
270 }
271}
272
273/* calculate area width */
274static inline u16 __tcm_area_width(struct tcm_area *area)
275{
276 return area->p1.x - area->p0.x + 1;
277}
278
279/* calculate area height */
280static inline u16 __tcm_area_height(struct tcm_area *area)
281{
282 return area->p1.y - area->p0.y + 1;
283}
284
285/* calculate number of slots in an area */
286static inline u16 __tcm_sizeof(struct tcm_area *area)
287{
288 return area->is2d ?
289 __tcm_area_width(area) * __tcm_area_height(area) :
290 (area->p1.x - area->p0.x + 1) + (area->p1.y - area->p0.y) *
291 area->tcm->width;
292}
293#define tcm_sizeof(area) __tcm_sizeof(&(area))
294#define tcm_awidth(area) __tcm_area_width(&(area))
295#define tcm_aheight(area) __tcm_area_height(&(area))
296#define tcm_is_in(pt, area) __tcm_is_in(&(pt), &(area))
297
298/* limit a 1D area to the first N pages */
299static inline s32 tcm_1d_limit(struct tcm_area *a, u32 num_pg)
300{
301 if (__tcm_sizeof(a) < num_pg)
302 return -ENOMEM;
303 if (!num_pg)
304 return -EINVAL;
305
306 a->p1.x = (a->p0.x + num_pg - 1) % a->tcm->width;
307 a->p1.y = a->p0.y + ((a->p0.x + num_pg - 1) / a->tcm->width);
308 return 0;
309}
310
311/**
312 * Iterate through 2D slices of a valid area. Behaves
313 * syntactically as a for(;;) statement.
314 *
315 * @param var Name of a local variable of type 'struct
316 * tcm_area *' that will get modified to
317 * contain each slice.
318 * @param area Pointer to the VALID parent area. This
319 * structure will not get modified
320 * throughout the loop.
321 *
322 */
323#define tcm_for_each_slice(var, area, safe) \
324 for (safe = area, \
325 tcm_slice(&safe, &var); \
326 var.tcm; tcm_slice(&safe, &var))
327
328#endif