aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
authorRob Clark <rob@ti.com>2011-09-01 03:05:21 -0400
committerPaolo Pisati <paolo.pisati@canonical.com>2012-08-17 04:18:47 -0400
commit6ac5d0b980b1a9c43071cb1ab36aaf29bff6fd45 (patch)
tree3df728f370191334e723ffb715f153248e4ac8a4 /drivers/staging
parent07d9aa8a852d1e4437ed23a8d84926b4e0580f05 (diff)
Add omap drm display driver
A DSS based DRM display driver, which provides a plugin interface for 3d/2d accelerators to register. The core driver handles construction of CRTC/encoder/connectors to represent the hardware, and support KMS. This driver replaces omapfb. The omap drm driver allocates framebuffer memory and implements (with the help of drm_fb_helper) the legacy fbdev interface. The driver maps CRTCs to overlays, encoders to overlay-managers, and connectors to dssdev's. Signed-off-by: Rob Clark <rob@ti.com> Signed-off-by: Ricardo Salveti de Araujo <ricardo.salveti@canonical.com>
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/omapdrm/Kconfig25
-rw-r--r--drivers/staging/omapdrm/Makefile8
-rw-r--r--drivers/staging/omapdrm/TODO.txt10
-rw-r--r--drivers/staging/omapdrm/omap_connector.c398
-rw-r--r--drivers/staging/omapdrm/omap_crtc.c325
-rw-r--r--drivers/staging/omapdrm/omap_drv.c751
-rw-r--r--drivers/staging/omapdrm/omap_drv.h68
-rw-r--r--drivers/staging/omapdrm/omap_encoder.c198
-rw-r--r--drivers/staging/omapdrm/omap_fb.c251
-rw-r--r--drivers/staging/omapdrm/omap_fbdev.c279
12 files changed, 2316 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 04d35130e9f..a9a422575bb 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -162,4 +162,6 @@ source "drivers/staging/mei/Kconfig"
162 162
163source "drivers/staging/nvec/Kconfig" 163source "drivers/staging/nvec/Kconfig"
164 164
165source "drivers/staging/omapdrm/Kconfig"
166
165endif # STAGING 167endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index db56670ec3a..db8a6500b67 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -72,3 +72,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
72obj-$(CONFIG_DRM_PSB) += gma500/ 72obj-$(CONFIG_DRM_PSB) += gma500/
73obj-$(CONFIG_INTEL_MEI) += mei/ 73obj-$(CONFIG_INTEL_MEI) += mei/
74obj-$(CONFIG_MFD_NVEC) += nvec/ 74obj-$(CONFIG_MFD_NVEC) += nvec/
75obj-$(CONFIG_DRM_OMAP) += omapdrm/
diff --git a/drivers/staging/omapdrm/Kconfig b/drivers/staging/omapdrm/Kconfig
new file mode 100644
index 00000000000..513ff916075
--- /dev/null
+++ b/drivers/staging/omapdrm/Kconfig
@@ -0,0 +1,25 @@
1
2config DRM_OMAP
3 tristate "OMAP DRM (EXPERIMENTAL)"
4 depends on DRM && !CONFIG_FB_OMAP2
5 select DRM_KMS_HELPER
6 select OMAP2_VRAM
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 y
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/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile
new file mode 100644
index 00000000000..0fd44de2ce3
--- /dev/null
+++ b/drivers/staging/omapdrm/Makefile
@@ -0,0 +1,8 @@
1#
2# Makefile for the drm device driver. This driver provides support for the
3# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
4
5ccflags-y := -Iinclude/drm -Werror
6omapdrm-y := omap_drv.o omap_crtc.o omap_encoder.o omap_connector.o omap_fb.o omap_fbdev.o
7
8obj-$(CONFIG_DRM_OMAP) += omapdrm.o
diff --git a/drivers/staging/omapdrm/TODO.txt b/drivers/staging/omapdrm/TODO.txt
new file mode 100644
index 00000000000..65a2c8239a2
--- /dev/null
+++ b/drivers/staging/omapdrm/TODO.txt
@@ -0,0 +1,10 @@
1+ check error handling/cleanup paths
2+ GEM buffer support.. don't ignore bo id in omap_fb
3
4+ plugins should register consecutive ioctl's relative to a base ioctl
5 # that is assigned by omap_drm
6+ omap_drm should have an ioctl to query by plugin name the assigned
7 base ioctl #
8+ userspace should use this base ioctl # to calculate the actual ioctl
9 # relative to that base.
10
diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/staging/omapdrm/omap_connector.c
new file mode 100644
index 00000000000..f0c30213609
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_connector.c
@@ -0,0 +1,398 @@
1/*
2 * linux/drivers/staging/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 <linux/omap_drm.h>
21#include "omap_drv.h"
22
23#include "drm_crtc.h"
24#include "drm_crtc_helper.h"
25
26/*
27 * connector funcs
28 */
29
30#define to_omap_connector(x) container_of(x, struct omap_connector, base)
31
32struct omap_connector {
33 struct drm_connector base;
34 struct omap_dss_device *dssdev;
35};
36
37static inline void 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 /* note: whether or not it is interlaced, +/- h/vsync, etc,
53 * which should be set in the mode flags, is not exposed in
54 * the omap_video_timings struct.. but hdmi driver tracks
55 * those separately so all we have to have to set the mode
56 * is the way to recover these timings values, and the
57 * omap_dss_driver would do the rest.
58 */
59}
60
61static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
62 struct drm_display_mode *mode)
63{
64 timings->pixel_clock = mode->clock;
65
66 timings->x_res = mode->hdisplay;
67 timings->hfp = mode->hsync_start - mode->hdisplay;
68 timings->hsw = mode->hsync_end - mode->hsync_start;
69 timings->hbp = mode->htotal - mode->hsync_end;
70
71 timings->y_res = mode->vdisplay;
72 timings->vfp = mode->vsync_start - mode->vdisplay;
73 timings->vsw = mode->vsync_end - mode->vsync_start;
74 timings->vbp = mode->vtotal - mode->vsync_end;
75}
76
77void omap_connector_dpms(struct drm_connector *connector, int mode)
78{
79 struct omap_connector *omap_connector = to_omap_connector(connector);
80 struct omap_dss_device *dssdev = omap_connector->dssdev;
81
82 /* TODO: add API in DSS to suspend/resume individual displays.. */
83
84 DBG("%s: %d", dssdev->name, mode);
85}
86
87enum drm_connector_status omap_connector_detect(
88 struct drm_connector *connector, bool force)
89{
90 struct omap_connector *omap_connector = to_omap_connector(connector);
91 struct omap_dss_device *dssdev = omap_connector->dssdev;
92 struct omap_dss_driver *dssdrv = dssdev->driver;
93 enum drm_connector_status ret;
94
95 if (dssdrv->is_detected(dssdev)) {
96 ret = connector_status_connected;
97 } else {
98 ret = connector_status_disconnected;
99 }
100
101 VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
102
103 return ret;
104}
105
106static void omap_connector_destroy(struct drm_connector *connector)
107{
108 struct omap_connector *omap_connector = to_omap_connector(connector);
109
110 DBG("%s", omap_connector->dssdev->name);
111 drm_sysfs_connector_remove(connector);
112 drm_connector_cleanup(connector);
113 kfree(omap_connector);
114}
115
116#define MAX_EDID 256
117
118static int omap_connector_get_modes(struct drm_connector *connector)
119{
120 struct omap_connector *omap_connector = to_omap_connector(connector);
121 struct omap_dss_device *dssdev = omap_connector->dssdev;
122 struct omap_dss_driver *dssdrv = dssdev->driver;
123 struct drm_device *dev = connector->dev;
124 int n = 0;
125
126 DBG("%s", omap_connector->dssdev->name);
127
128 /* if display exposes EDID, then we parse that in the normal way to
129 * build table of supported modes.. otherwise (ie. fixed resolution
130 * LCD panels) we just return a single mode corresponding to the
131 * currently configured timings:
132 */
133 if (dssdrv->get_edid) {
134 void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
135
136 if ((dssdrv->get_edid(dssdev, edid, MAX_EDID) == 0) &&
137 drm_edid_is_valid(edid)) {
138 drm_mode_connector_update_edid_property(connector, edid);
139 n = drm_add_edid_modes(connector, edid);
140 kfree(connector->display_info.raw_edid);
141 connector->display_info.raw_edid = edid;
142 } else {
143 drm_mode_connector_update_edid_property(connector, NULL);
144 connector->display_info.raw_edid = NULL;
145 kfree(edid);
146 }
147 } else {
148 struct drm_display_mode *mode = drm_mode_create(dev);
149 struct omap_video_timings timings;
150
151 dssdrv->get_timings(dssdev, &timings);
152
153 copy_timings_omap_to_drm(mode, &timings);
154
155 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
156 drm_mode_set_name(mode);
157 drm_mode_probed_add(connector, mode);
158
159 n = 1;
160 }
161
162 return n;
163}
164
165static int omap_connector_mode_valid(struct drm_connector *connector,
166 struct drm_display_mode *mode)
167{
168 struct omap_connector *omap_connector = to_omap_connector(connector);
169 struct omap_dss_device *dssdev = omap_connector->dssdev;
170 struct omap_dss_driver *dssdrv = dssdev->driver;
171 struct omap_video_timings timings = {0};
172 struct drm_device *dev = connector->dev;
173 struct drm_display_mode *new_mode;
174 int ret = MODE_BAD;
175
176 copy_timings_drm_to_omap(&timings, mode);
177 mode->vrefresh = drm_mode_vrefresh(mode);
178
179 if (!dssdrv->check_timings(dssdev, &timings)) {
180 /* check if vrefresh is still valid */
181 new_mode = drm_mode_duplicate(dev, mode);
182 new_mode->clock = timings.pixel_clock;
183 new_mode->vrefresh = 0;
184 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
185 ret = MODE_OK;
186 drm_mode_destroy(dev, new_mode);
187 }
188
189 DBG("connector: mode %s: "
190 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
191 (ret == MODE_OK) ? "valid" : "invalid",
192 mode->base.id, mode->name, mode->vrefresh, mode->clock,
193 mode->hdisplay, mode->hsync_start,
194 mode->hsync_end, mode->htotal,
195 mode->vdisplay, mode->vsync_start,
196 mode->vsync_end, mode->vtotal, mode->type, mode->flags);
197
198 return ret;
199}
200
201struct drm_encoder * omap_connector_attached_encoder(
202 struct drm_connector *connector)
203{
204 int i;
205 struct omap_connector *omap_connector = to_omap_connector(connector);
206
207 for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
208 struct drm_mode_object *obj;
209
210 if (connector->encoder_ids[i] == 0)
211 break;
212
213 obj = drm_mode_object_find(connector->dev,
214 connector->encoder_ids[i],
215 DRM_MODE_OBJECT_ENCODER);
216
217 if (obj) {
218 struct drm_encoder *encoder = obj_to_encoder(obj);
219 struct omap_overlay_manager *mgr =
220 omap_encoder_get_manager(encoder);
221 DBG("%s: found %s", omap_connector->dssdev->name,
222 mgr->name);
223 return encoder;
224 }
225 }
226
227 DBG("%s: no encoder", omap_connector->dssdev->name);
228
229 return NULL;
230}
231
232static const struct drm_connector_funcs omap_connector_funcs = {
233 .dpms = drm_helper_connector_dpms,
234 .detect = omap_connector_detect,
235 .fill_modes = drm_helper_probe_single_connector_modes,
236 .destroy = omap_connector_destroy,
237};
238
239static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
240 .get_modes = omap_connector_get_modes,
241 .mode_valid = omap_connector_mode_valid,
242 .best_encoder = omap_connector_attached_encoder,
243};
244
245/* called from encoder when mode is set, to propagate settings to the dssdev */
246void omap_connector_mode_set(struct drm_connector *connector,
247 struct drm_display_mode *mode)
248{
249 struct drm_device *dev = connector->dev;
250 struct omap_connector *omap_connector = to_omap_connector(connector);
251 struct omap_dss_device *dssdev = omap_connector->dssdev;
252 struct omap_dss_driver *dssdrv = dssdev->driver;
253 struct omap_video_timings timings;
254
255 copy_timings_drm_to_omap(&timings, mode);
256
257 DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
258 omap_connector->dssdev->name,
259 mode->base.id, mode->name, mode->vrefresh, mode->clock,
260 mode->hdisplay, mode->hsync_start,
261 mode->hsync_end, mode->htotal,
262 mode->vdisplay, mode->vsync_start,
263 mode->vsync_end, mode->vtotal, mode->type, mode->flags);
264
265 if (dssdrv->check_timings(dssdev, &timings)) {
266 dev_err(dev->dev, "could not set timings\n");
267 return;
268 }
269
270 dssdrv->set_timings(dssdev, &timings);
271}
272
273enum omap_dss_update_mode omap_connector_get_update_mode(
274 struct drm_connector *connector)
275{
276 struct omap_connector *omap_connector = to_omap_connector(connector);
277 struct omap_dss_device *dssdev = omap_connector->dssdev;
278 struct omap_dss_driver *dssdrv = dssdev->driver;
279
280 DBG("%s", omap_connector->dssdev->name);
281
282 if (dssdrv->get_update_mode) {
283 return dssdrv->get_update_mode(dssdev);
284 }
285
286 return -1;
287}
288EXPORT_SYMBOL(omap_connector_get_update_mode);
289
290int omap_connector_set_update_mode(struct drm_connector *connector,
291 enum omap_dss_update_mode mode)
292{
293 struct omap_connector *omap_connector = to_omap_connector(connector);
294 struct omap_dss_device *dssdev = omap_connector->dssdev;
295 struct omap_dss_driver *dssdrv = dssdev->driver;
296
297 DBG("%s: %d", omap_connector->dssdev->name, mode);
298
299 if (dssdrv->set_update_mode) {
300 return dssdrv->set_update_mode(dssdev, mode);
301 }
302
303 return -EINVAL;
304}
305EXPORT_SYMBOL(omap_connector_set_update_mode);
306
307int omap_connector_sync(struct drm_connector *connector)
308{
309 struct omap_connector *omap_connector = to_omap_connector(connector);
310 struct omap_dss_device *dssdev = omap_connector->dssdev;
311 struct omap_dss_driver *dssdrv = dssdev->driver;
312
313 DBG("%s", omap_connector->dssdev->name);
314
315 if (dssdrv->sync) {
316 return dssdrv->sync(dssdev);
317 }
318
319 return -EINVAL;
320}
321EXPORT_SYMBOL(omap_connector_sync);
322
323/* flush an area of the framebuffer (in case of manual update display that
324 * is not automatically flushed)
325 */
326void omap_connector_flush(struct drm_connector *connector,
327 int x, int y, int w, int h)
328{
329 struct omap_connector *omap_connector = to_omap_connector(connector);
330
331 /* TODO: enable when supported in dss */
332 VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
333}
334
335/* initialize connector */
336struct drm_connector * omap_connector_init(struct drm_device *dev,
337 int connector_type, struct omap_dss_device *dssdev)
338{
339 struct drm_connector *connector = NULL;
340 struct omap_connector *omap_connector;
341
342 DBG("%s", dssdev->name);
343
344 omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
345 if (!omap_connector) {
346 dev_err(dev->dev, "could not allocate connector\n");
347 goto fail;
348 }
349
350 omap_connector->dssdev = dssdev;
351 connector = &omap_connector->base;
352
353 drm_connector_init(dev, connector, &omap_connector_funcs,
354 connector_type);
355 drm_connector_helper_add(connector, &omap_connector_helper_funcs);
356
357#if 0 /* enable when dss2 supports hotplug */
358 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD)
359 connector->polled = 0;
360 else
361#endif
362 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
363 DRM_CONNECTOR_POLL_DISCONNECT;
364
365 connector->interlace_allowed = 1;
366 connector->doublescan_allowed = 0;
367
368 drm_sysfs_connector_add(connector);
369
370 /* store resume info for suspended displays */
371 switch (dssdev->state) {
372 case OMAP_DSS_DISPLAY_SUSPENDED:
373 dssdev->activate_after_resume = true;
374 break;
375 case OMAP_DSS_DISPLAY_DISABLED:
376 if (dssdev->driver) {
377 int ret = dssdev->driver->enable(dssdev);
378 if (ret) {
379 DBG("%s: failed to enable: %d",
380 dssdev->name, ret);
381 dssdev->driver->disable(dssdev);
382 goto fail;
383 }
384 }
385 break;
386 default:
387 break;
388 }
389
390 return connector;
391
392fail:
393 if (connector) {
394 omap_connector_destroy(connector);
395 }
396
397 return NULL;
398}
diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
new file mode 100644
index 00000000000..98c263373e8
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -0,0 +1,325 @@
1/*
2 * linux/drivers/staging/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 <linux/omap_drm.h>
21#include "omap_drv.h"
22
23#include "drm_mode.h"
24#include "drm_crtc.h"
25#include "drm_crtc_helper.h"
26
27#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
28
29struct omap_crtc {
30 struct drm_crtc base;
31 struct omap_overlay *ovl;
32 struct omap_overlay_info info;
33 int id;
34};
35
36/* push changes down to dss2 */
37static int commit(struct drm_crtc *crtc)
38{
39 struct drm_device *dev = crtc->dev;
40 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
41 struct omap_overlay *ovl = omap_crtc->ovl;
42 struct omap_overlay_info *info = &omap_crtc->info;
43 int ret;
44
45 DBG("%s", omap_crtc->ovl->name);
46 DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
47 info->out_height, info->screen_width);
48 DBG("%d,%d %p %08x", info->pos_x, info->pos_y, info->vaddr,
49 info->paddr);
50
51 /* NOTE: do we want to do this at all here, or just wait
52 * for dpms(ON) since other CRTC's may not have their mode
53 * set yet, so fb dimensions may still change..
54 */
55 ret = ovl->set_overlay_info(ovl, info);
56 if (ret) {
57 dev_err(dev->dev, "could not set overlay info\n");
58 return ret;
59 }
60
61 /* our encoder doesn't necessarily get a commit() after this, in
62 * particular in the dpms() and mode_set_base() cases, so force the
63 * manager to update:
64 *
65 * could this be in the encoder somehow?
66 */
67 if (ovl->manager) {
68 ret = ovl->manager->apply(ovl->manager);
69 if (ret) {
70 dev_err(dev->dev, "could not apply\n");
71 return ret;
72 }
73 }
74
75 if (info->enabled) {
76 omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y,
77 crtc->fb->width, crtc->fb->height);
78 }
79
80 return 0;
81}
82
83/* update parameters that are dependent on the framebuffer dimensions and
84 * position within the fb that this crtc scans out from. This is called
85 * when framebuffer dimensions or x,y base may have changed, either due
86 * to our mode, or a change in another crtc that is scanning out of the
87 * same fb.
88 */
89static void update_scanout(struct drm_crtc *crtc)
90{
91 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
92 unsigned long paddr;
93 void __iomem *vaddr;
94 int screen_width;
95
96 omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y,
97 &vaddr, &paddr, &screen_width);
98
99 DBG("%s: %d,%d: %p %08lx (%d)", omap_crtc->ovl->name,
100 crtc->x, crtc->y, vaddr, paddr, screen_width);
101
102 omap_crtc->info.paddr = paddr;
103 omap_crtc->info.vaddr = vaddr;
104 omap_crtc->info.screen_width = screen_width;
105}
106
107static void omap_crtc_gamma_set(struct drm_crtc *crtc,
108 u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size)
109{
110 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
111 DBG("%s", omap_crtc->ovl->name);
112 // XXX ignore?
113}
114
115static void omap_crtc_destroy(struct drm_crtc *crtc)
116{
117 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
118 DBG("%s", omap_crtc->ovl->name);
119 drm_crtc_cleanup(crtc);
120 kfree(omap_crtc);
121}
122
123static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
124{
125 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
126
127 DBG("%s: %d", omap_crtc->ovl->name, mode);
128
129 if (mode == DRM_MODE_DPMS_ON) {
130 update_scanout(crtc);
131 omap_crtc->info.enabled = true;
132 } else {
133 omap_crtc->info.enabled = false;
134 }
135
136 commit(crtc);
137}
138
139static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
140 struct drm_display_mode *mode,
141 struct drm_display_mode *adjusted_mode)
142{
143 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
144 // XXX I guess we support anything?
145 DBG("%s", omap_crtc->ovl->name);
146 return true;
147}
148
149static int omap_crtc_mode_set(struct drm_crtc *crtc,
150 struct drm_display_mode *mode,
151 struct drm_display_mode *adjusted_mode,
152 int x, int y,
153 struct drm_framebuffer *old_fb)
154{
155 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
156
157 DBG("%s: %d,%d: %dx%d",omap_crtc->ovl->name, x, y,
158 mode->hdisplay, mode->vdisplay);
159
160 /* just use adjusted mode */
161 mode = adjusted_mode;
162
163 omap_crtc->info.width = mode->hdisplay;
164 omap_crtc->info.height = mode->vdisplay;
165 omap_crtc->info.out_width = mode->hdisplay;
166 omap_crtc->info.out_height = mode->vdisplay;
167 omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U;
168 omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA;
169 omap_crtc->info.rotation = OMAP_DSS_ROT_0;
170 omap_crtc->info.global_alpha = 0xff;
171 omap_crtc->info.mirror = 0;
172 omap_crtc->info.mirror = 0;
173 omap_crtc->info.pos_x = 0;
174 omap_crtc->info.pos_y = 0;
175#if 0 /* re-enable when these are available in DSS2 driver */
176 omap_crtc->info.zorder = 3; /* GUI in the front, video behind */
177 omap_crtc->info.min_x_decim = 1;
178 omap_crtc->info.max_x_decim = 1;
179 omap_crtc->info.min_y_decim = 1;
180 omap_crtc->info.max_y_decim = 1;
181#endif
182
183 update_scanout(crtc);
184
185 return 0;
186}
187
188static void omap_crtc_prepare(struct drm_crtc *crtc)
189{
190 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
191 struct omap_overlay *ovl = omap_crtc->ovl;
192
193 DBG("%s", omap_crtc->ovl->name);
194
195 ovl->get_overlay_info(ovl, &omap_crtc->info);
196
197 omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
198}
199
200static void omap_crtc_commit(struct drm_crtc *crtc)
201{
202 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
203 DBG("%s", omap_crtc->ovl->name);
204 omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
205}
206
207static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
208 struct drm_framebuffer *old_fb)
209{
210 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
211
212 DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb);
213
214 update_scanout(crtc);
215
216 return commit(crtc);
217}
218
219static void omap_crtc_load_lut(struct drm_crtc *crtc)
220{
221 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
222 DBG("%s", omap_crtc->ovl->name);
223}
224
225static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
226 struct drm_framebuffer *fb,
227 struct drm_pending_vblank_event *event)
228{
229 struct drm_device *dev = crtc->dev;
230 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
231 struct timeval now;
232 unsigned long flags;
233 int ret;
234
235 DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
236
237 crtc->fb = fb;
238
239 update_scanout(crtc);
240 ret = commit(crtc);
241
242 /* wakeup userspace */
243 // TODO: this should happen *after* flip.. somehow..
244 if (event) {
245 spin_lock_irqsave(&dev->event_lock, flags);
246 event->event.sequence =
247 drm_vblank_count_and_time(dev, omap_crtc->id, &now);
248 event->event.tv_sec = now.tv_sec;
249 event->event.tv_usec = now.tv_usec;
250 list_add_tail(&event->base.link,
251 &event->base.file_priv->event_list);
252 wake_up_interruptible(&event->base.file_priv->event_wait);
253 spin_unlock_irqrestore(&dev->event_lock, flags);
254 }
255
256 return ret;
257}
258
259int omap_crtc_page_flip(struct drm_crtc *crtc,
260 struct drm_framebuffer *fb,
261 struct drm_pending_vblank_event *event)
262{
263 struct drm_device *dev = crtc->dev;
264 int ret;
265
266 mutex_lock(&dev->mode_config.mutex);
267 ret = omap_crtc_page_flip_locked(crtc, fb, event);
268 mutex_unlock(&dev->mode_config.mutex);
269
270 return ret;
271}
272EXPORT_SYMBOL(omap_crtc_page_flip);
273
274static const struct drm_crtc_funcs omap_crtc_funcs = {
275 .gamma_set = omap_crtc_gamma_set,
276 .set_config = drm_crtc_helper_set_config,
277 .destroy = omap_crtc_destroy,
278 .page_flip = omap_crtc_page_flip_locked,
279};
280
281static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
282 .dpms = omap_crtc_dpms,
283 .mode_fixup = omap_crtc_mode_fixup,
284 .mode_set = omap_crtc_mode_set,
285 .prepare = omap_crtc_prepare,
286 .commit = omap_crtc_commit,
287 .mode_set_base = omap_crtc_mode_set_base,
288 .load_lut = omap_crtc_load_lut,
289};
290
291struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc)
292{
293 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
294 return omap_crtc->ovl;
295}
296
297/* initialize crtc */
298struct drm_crtc * omap_crtc_init(struct drm_device *dev,
299 struct omap_overlay *ovl, int id)
300{
301 struct drm_crtc *crtc = NULL;
302 struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
303
304 DBG("%s", ovl->name);
305
306 if (!omap_crtc) {
307 dev_err(dev->dev, "could not allocate CRTC\n");
308 goto fail;
309 }
310
311 omap_crtc->ovl = ovl;
312 omap_crtc->id = id;
313 crtc = &omap_crtc->base;
314 drm_crtc_init(dev, crtc, &omap_crtc_funcs);
315 drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
316
317 return crtc;
318
319fail:
320 if (crtc) {
321 drm_crtc_cleanup(crtc);
322 kfree(omap_crtc);
323 }
324 return NULL;
325}
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
new file mode 100644
index 00000000000..fc2d4e9fa92
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -0,0 +1,751 @@
1/*
2 * linux/drivers/staging/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 <linux/omap_drm.h>
21#include "omap_drv.h"
22
23#include "drm_crtc_helper.h"
24#include "drm_fb_helper.h"
25
26#define DRIVER_NAME MODULE_NAME
27#define DRIVER_DESC "OMAP DRM"
28#define DRIVER_DATE "20110403"
29#define DRIVER_MAJOR 1
30#define DRIVER_MINOR 0
31#define DRIVER_PATCHLEVEL 0
32
33struct drm_device *drm_device;
34
35/* TODO: think about how to handle more than one plugin.. ie. some ops
36 * me might want to stop on the first plugin that doesn't return an
37 * error, etc..
38 */
39LIST_HEAD(plugin_list);
40
41/* keep track of whether we are already loaded.. we may need to call
42 * plugin's load() if they register after we are already loaded
43 */
44static bool loaded = false;
45
46/*
47 * mode config funcs
48 */
49
50/* Notes about mapping DSS and DRM entities:
51 * CRTC: overlay
52 * encoder: manager.. with some extension to allow one primary CRTC
53 * and zero or more video CRTC's to be mapped to one encoder?
54 * connector: dssdev.. manager can be attached/detached from different
55 * devices
56 */
57
58static void omap_fb_output_poll_changed(struct drm_device *dev)
59{
60 struct omap_drm_private *priv = dev->dev_private;
61 DBG("dev=%p", dev);
62 if (priv->fbdev) {
63 drm_fb_helper_hotplug_event(priv->fbdev);
64 }
65}
66
67static struct drm_mode_config_funcs omap_mode_config_funcs = {
68 .fb_create = omap_framebuffer_create,
69 .output_poll_changed = omap_fb_output_poll_changed,
70};
71
72static int get_connector_type(struct omap_dss_device *dssdev)
73{
74 switch (dssdev->type) {
75 case OMAP_DISPLAY_TYPE_HDMI:
76 return DRM_MODE_CONNECTOR_HDMIA;
77 case OMAP_DISPLAY_TYPE_DPI:
78 if (!strcmp(dssdev->name, "dvi"))
79 return DRM_MODE_CONNECTOR_DVID;
80 default:
81 return DRM_MODE_CONNECTOR_Unknown;
82 }
83}
84
85#if 0 /* enable when dss2 supports hotplug */
86static int omap_drm_notifier(struct notifier_block *nb,
87 unsigned long evt, void *arg)
88{
89 switch (evt) {
90 case OMAP_DSS_SIZE_CHANGE:
91 case OMAP_DSS_HOTPLUG_CONNECT:
92 case OMAP_DSS_HOTPLUG_DISCONNECT: {
93 struct drm_device *dev = drm_device;
94 DBG("hotplug event: evt=%d, dev=%p", evt, dev);
95 if (dev) {
96 drm_sysfs_hotplug_event(dev);
97 }
98 return NOTIFY_OK;
99 }
100 default: /* don't care about other events for now */
101 return NOTIFY_DONE;
102 }
103}
104#endif
105
106static void dump_video_chains(void)
107{
108 int i;
109
110 DBG("dumping video chains: ");
111 for (i = 0; i < omap_dss_get_num_overlays(); i++) {
112 struct omap_overlay *ovl = omap_dss_get_overlay(i);
113 struct omap_overlay_manager *mgr = ovl->manager;
114 struct omap_dss_device *dssdev = mgr ? mgr->device : NULL;
115 if (dssdev) {
116 DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
117 dssdev->name);
118 } else if (mgr) {
119 DBG("%d: %s -> %s", i, ovl->name, mgr->name);
120 } else {
121 DBG("%d: %s", i, ovl->name);
122 }
123 }
124}
125
126static int omap_modeset_init(struct drm_device *dev)
127{
128 const struct omap_drm_platform_data *pdata = dev->dev->platform_data;
129 struct omap_drm_private *priv = dev->dev_private;
130 struct omap_dss_device *dssdev = NULL;
131 int i, j;
132 unsigned int connected_connectors = 0;
133
134 /* create encoders for each manager */
135 int create_encoder(int i) {
136 struct omap_overlay_manager *mgr =
137 omap_dss_get_overlay_manager(i);
138 struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
139
140 if (!encoder) {
141 dev_err(dev->dev, "could not create encoder\n");
142 return -ENOMEM;
143 }
144
145 priv->encoders[priv->num_encoders++] = encoder;
146
147 return 0;
148 }
149
150 /* create connectors for each display device */
151 int create_connector(struct omap_dss_device *dssdev) {
152 static struct notifier_block *notifier;
153 struct drm_connector *connector;
154
155 if (!dssdev->driver) {
156 dev_warn(dev->dev, "%s has no driver.. skipping it\n",
157 dssdev->name);
158 return 0;
159 }
160
161 if (!(dssdev->driver->get_timings ||
162 dssdev->driver->get_edid)) {
163 dev_warn(dev->dev, "%s driver does not support "
164 "get_timings or get_edid.. skipping it!\n",
165 dssdev->name);
166 return 0;
167 }
168
169 omap_dss_get_device(dssdev);
170
171 connector = omap_connector_init(dev,
172 get_connector_type(dssdev), dssdev);
173
174 if (!connector) {
175 dev_err(dev->dev, "could not create connector\n");
176 return -ENOMEM;
177 }
178
179 /* track what is already connected.. rather than looping thru
180 * all connectors twice later, first for connected then for
181 * remainder (which could be a race condition if connected
182 * status changes)
183 */
184 if (omap_connector_detect(connector, true) ==
185 connector_status_connected) {
186 connected_connectors |= (1 << priv->num_connectors);
187 }
188
189 priv->connectors[priv->num_connectors++] = connector;
190
191#if 0 /* enable when dss2 supports hotplug */
192 notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
193 notifier->notifier_call = omap_drm_notifier;
194 omap_dss_add_notify(dssdev, notifier);
195#else
196 notifier = NULL;
197#endif
198
199 for (j = 0; j < priv->num_encoders; j++) {
200 struct omap_overlay_manager *mgr =
201 omap_encoder_get_manager(priv->encoders[j]);
202 if (mgr->device == dssdev) {
203 drm_mode_connector_attach_encoder(connector,
204 priv->encoders[j]);
205 }
206 }
207
208 return 0;
209 }
210
211 /* create up to max_overlays CRTCs mapping to overlays.. by default,
212 * connect the overlays to different managers/encoders, giving priority
213 * to encoders connected to connectors with a detected connection
214 */
215 int create_crtc(int i) {
216 struct omap_overlay *ovl = omap_dss_get_overlay(i);
217 struct omap_overlay_manager *mgr = NULL;
218 struct drm_crtc *crtc;
219
220 if (ovl->manager) {
221 DBG("disconnecting %s from %s", ovl->name,
222 ovl->manager->name);
223 ovl->unset_manager(ovl);
224 }
225
226 /* find next best connector, ones with detected connection first
227 */
228 while (j < priv->num_connectors && !mgr) {
229 if (connected_connectors & (1 << j)) {
230 struct drm_encoder * encoder =
231 omap_connector_attached_encoder(
232 priv->connectors[j]);
233 if (encoder) {
234 mgr = omap_encoder_get_manager(encoder);
235 }
236 }
237 j++;
238 }
239
240 /* if we couldn't find another connected connector, lets start
241 * looking at the unconnected connectors:
242 */
243 while (j < 2 * priv->num_connectors && !mgr) {
244 int idx = j - priv->num_connectors;
245 if (!(connected_connectors & (1 << idx))) {
246 struct drm_encoder * encoder =
247 omap_connector_attached_encoder(
248 priv->connectors[idx]);
249 if (encoder) {
250 mgr = omap_encoder_get_manager(encoder);
251 }
252 }
253 j++;
254 }
255
256 if (mgr) {
257 DBG("connecting %s to %s", ovl->name, mgr->name);
258 ovl->set_manager(ovl, mgr);
259 }
260
261 crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
262
263 if (!crtc) {
264 dev_err(dev->dev, "could not create CRTC\n");
265 return -ENOMEM;
266 }
267
268 priv->crtcs[priv->num_crtcs++] = crtc;
269
270 return 0;
271 }
272
273 drm_mode_config_init(dev);
274
275 if (pdata) {
276 /* if platform data is provided by the board file, use it to
277 * control which overlays, managers, and devices we own.
278 */
279 for (i = 0; i < pdata->mgr_cnt; i++) {
280 create_encoder(pdata->mgr_ids[i]);
281 }
282
283 for (i = 0; i < pdata->dev_cnt; i++) {
284 int m(struct omap_dss_device *dssdev, void *data) {
285 return ! strcmp(dssdev->name, data);
286 }
287 struct omap_dss_device *dssdev =
288 omap_dss_find_device(
289 (void *)pdata->dev_names[i], m);
290 if (!dssdev) {
291 dev_warn(dev->dev, "no such dssdev: %s\n",
292 pdata->dev_names[i]);
293 continue;
294 }
295 create_connector(dssdev);
296 }
297
298 j = 0;
299 for (i = 0; i < pdata->ovl_cnt; i++) {
300 create_crtc(pdata->ovl_ids[i]);
301 }
302 } else {
303 /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
304 * to make educated guesses about everything else
305 */
306 int max_overlays = min(omap_dss_get_num_overlays(),
307 CONFIG_DRM_OMAP_NUM_CRTCS);
308
309 for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
310 create_encoder(i);
311 }
312
313 for_each_dss_dev(dssdev) {
314 create_connector(dssdev);
315 }
316
317 j = 0;
318 for (i = 0; i < max_overlays; i++) {
319 create_crtc(i);
320 }
321 }
322
323 /* for now keep the mapping of CRTCs and encoders static.. */
324 for (i = 0; i < priv->num_encoders; i++) {
325 struct drm_encoder *encoder = priv->encoders[i];
326 struct omap_overlay_manager *mgr =
327 omap_encoder_get_manager(encoder);
328
329 encoder->possible_crtcs = 0;
330
331 for (j = 0; j < priv->num_crtcs; j++) {
332 struct omap_overlay *ovl =
333 omap_crtc_get_overlay(priv->crtcs[j]);
334 if (ovl->manager == mgr) {
335 encoder->possible_crtcs |= (1 << j);
336 }
337 }
338
339 DBG("%s: possible_crtcs=%08x", mgr->name,
340 encoder->possible_crtcs);
341 }
342
343 dump_video_chains();
344
345 dev->mode_config.min_width = 640;
346 dev->mode_config.min_height = 480;
347
348 /* note: pvr can't currently handle dst surfaces larger than 2k by 2k */
349 dev->mode_config.max_width = 2048;
350 dev->mode_config.max_height = 2048;
351
352 dev->mode_config.funcs = &omap_mode_config_funcs;
353
354 return 0;
355}
356
357/*
358 * drm driver funcs
359 */
360
361/**
362 * load - setup chip and create an initial config
363 * @dev: DRM device
364 * @flags: startup flags
365 *
366 * The driver load routine has to do several things:
367 * - initialize the memory manager
368 * - allocate initial config memory
369 * - setup the DRM framebuffer with the allocated memory
370 */
371static int dev_load(struct drm_device *dev, unsigned long flags)
372{
373 struct omap_drm_private *priv;
374 struct omap_drm_plugin *plugin;
375 int ret;
376
377 DBG("load: dev=%p", dev);
378
379 drm_device = dev;
380
381 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
382 if (!priv) {
383 dev_err(dev->dev, "could not allocate priv\n");
384 return -1;
385 }
386
387 dev->dev_private = priv;
388
389 ret = omap_modeset_init(dev);
390 if (ret) {
391 dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
392 // hmm
393 //return ret;
394 }
395
396 priv->fbdev = omap_fbdev_init(dev);
397 if (!priv->fbdev) {
398 dev_err(dev->dev, "omap_fbdev_init failed\n");
399 ret = -ENOMEM;
400 // hmm
401 //return ret;
402 }
403
404 drm_kms_helper_poll_init(dev);
405
406 ret = drm_vblank_init(dev, priv->num_crtcs);
407 if (ret) {
408 dev_err(dev->dev, "could not init vblank\n");
409 }
410
411 loaded = true;
412
413 list_for_each_entry(plugin, &plugin_list, list) {
414 ret = plugin->load(dev, flags);
415 }
416
417 return 0;
418}
419
420static int dev_unload(struct drm_device *dev)
421{
422 struct omap_drm_plugin *plugin;
423 int ret;
424
425 DBG("unload: dev=%p", dev);
426
427 list_for_each_entry(plugin, &plugin_list, list) {
428 ret = plugin->unload(dev);
429 }
430
431 drm_kms_helper_poll_fini(dev);
432 drm_vblank_cleanup(dev);
433
434 loaded = false;
435
436 return 0;
437}
438
439static int dev_open(struct drm_device *dev, struct drm_file *file)
440{
441 struct omap_drm_plugin *plugin;
442 bool found_pvr = false;
443 int ret;
444
445 file->driver_priv = NULL;
446
447 DBG("open: dev=%p, file=%p", dev, file);
448
449 list_for_each_entry(plugin, &plugin_list, list) {
450 if (!strcmp(DRIVER_NAME "_pvr", plugin->name)) {
451 found_pvr = true;
452 break;
453 }
454 }
455
456 if (!found_pvr) {
457 DBG("open: PVR submodule not loaded.. let's try now");
458 request_module(DRIVER_NAME "_pvr");
459 }
460
461 list_for_each_entry(plugin, &plugin_list, list) {
462 ret = plugin->open(dev, file);
463 }
464
465 return 0;
466}
467
468static int dev_firstopen(struct drm_device *dev)
469{
470 DBG("firstopen: dev=%p", dev);
471 return 0;
472}
473
474/**
475 * lastclose - clean up after all DRM clients have exited
476 * @dev: DRM device
477 *
478 * Take care of cleaning up after all DRM clients have exited. In the
479 * mode setting case, we want to restore the kernel's initial mode (just
480 * in case the last client left us in a bad state).
481 *
482 * Additionally, in the non-mode setting case, we'll tear down the AGP
483 * and DMA structures, since the kernel won't be using them, and clean
484 * up any GEM state.
485 */
486static void dev_lastclose(struct drm_device * dev)
487{
488 DBG("lastclose: dev=%p", dev);
489}
490
491static void dev_preclose(struct drm_device * dev, struct drm_file *file)
492{
493 DBG("preclose: dev=%p", dev);
494}
495
496static void dev_postclose(struct drm_device *dev, struct drm_file *file)
497{
498 struct omap_drm_plugin *plugin;
499 int ret;
500
501 DBG("postclose: dev=%p, file=%p", dev, file);
502
503 list_for_each_entry(plugin, &plugin_list, list) {
504 ret = plugin->release(dev, file);
505 }
506
507 return;
508}
509
510/**
511 * enable_vblank - enable vblank interrupt events
512 * @dev: DRM device
513 * @crtc: which irq to enable
514 *
515 * Enable vblank interrupts for @crtc. If the device doesn't have
516 * a hardware vblank counter, this routine should be a no-op, since
517 * interrupts will have to stay on to keep the count accurate.
518 *
519 * RETURNS
520 * Zero on success, appropriate errno if the given @crtc's vblank
521 * interrupt cannot be enabled.
522 */
523static int dev_enable_vblank(struct drm_device *dev, int crtc)
524{
525 DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
526 return 0;
527}
528
529/**
530 * disable_vblank - disable vblank interrupt events
531 * @dev: DRM device
532 * @crtc: which irq to enable
533 *
534 * Disable vblank interrupts for @crtc. If the device doesn't have
535 * a hardware vblank counter, this routine should be a no-op, since
536 * interrupts will have to stay on to keep the count accurate.
537 */
538static void dev_disable_vblank(struct drm_device *dev, int crtc)
539{
540 DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
541}
542
543/**
544 * Called by \c drm_device_is_agp. Typically used to determine if a
545 * card is really attached to AGP or not.
546 *
547 * \param dev DRM device handle
548 *
549 * \returns
550 * One of three values is returned depending on whether or not the
551 * card is absolutely \b not AGP (return of 0), absolutely \b is AGP
552 * (return of 1), or may or may not be AGP (return of 2).
553 */
554static int dev_device_is_agp(struct drm_device *dev)
555{
556 return 0;
557}
558
559static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
560{
561 return IRQ_HANDLED;
562}
563
564static void dev_irq_preinstall(struct drm_device *dev)
565{
566 DBG("irq_preinstall: dev=%p", dev);
567}
568
569static int dev_irq_postinstall(struct drm_device *dev)
570{
571 DBG("irq_postinstall: dev=%p", dev);
572 return 0;
573}
574
575static void dev_irq_uninstall(struct drm_device *dev)
576{
577 DBG("irq_uninstall: dev=%p", dev);
578}
579
580static int fop_mmap(struct file *file, struct vm_area_struct *vma)
581{
582 struct omap_drm_plugin *plugin;
583 int ret = 0;
584
585 list_for_each_entry(plugin, &plugin_list, list) {
586 ret = plugin->mmap(file, vma);
587 if (!ret) {
588 /* on first plugin that succeeds, bail out of iteration */
589 return ret;
590 }
591 }
592
593 return ret;
594}
595
596struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {{0}};
597
598static struct drm_driver omap_drm_driver = {
599 .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET,
600 .load = dev_load,
601 .unload = dev_unload,
602 .open = dev_open,
603 .firstopen = dev_firstopen,
604 .lastclose = dev_lastclose,
605 .preclose = dev_preclose,
606 .postclose = dev_postclose,
607 .get_vblank_counter = drm_vblank_count,
608 .enable_vblank = dev_enable_vblank,
609 .disable_vblank = dev_disable_vblank,
610 .device_is_agp = dev_device_is_agp,
611 .irq_preinstall = dev_irq_preinstall,
612 .irq_postinstall = dev_irq_postinstall,
613 .irq_uninstall = dev_irq_uninstall,
614 .irq_handler = dev_irq_handler,
615 .reclaim_buffers = drm_core_reclaim_buffers,
616 .ioctls = ioctls,
617 .num_ioctls = 0,
618 .fops = {
619 .owner = THIS_MODULE,
620 .open = drm_open,
621 .unlocked_ioctl = drm_ioctl,
622 .release = drm_release,
623 .mmap = fop_mmap,
624 .poll = drm_poll,
625 .fasync = drm_fasync,
626 .read = drm_read,
627 },
628 .name = DRIVER_NAME,
629 .desc = DRIVER_DESC,
630 .date = DRIVER_DATE,
631 .major = DRIVER_MAJOR,
632 .minor = DRIVER_MINOR,
633 .patchlevel = DRIVER_PATCHLEVEL,
634};
635
636int omap_drm_register_plugin(struct omap_drm_plugin *plugin)
637{
638 struct drm_device *dev = drm_device;
639 int i;
640
641 DBG("register plugin: %p (%s)", plugin, plugin->name);
642
643 /* XXX: PVR code uses drm_file->driver_priv...
644 * need to come up with some sane way to handle this.
645 */
646
647 list_add_tail(&plugin->list, &plugin_list);
648
649 /* register the plugin's ioctl's */
650 for (i = 0; i < plugin->num_ioctls; i++) {
651 int nr = i + plugin->ioctl_start;
652
653 /* check for out of bounds ioctl nr or already registered ioctl */
654 if (nr > ARRAY_SIZE(ioctls) || ioctls[nr].func) {
655 dev_err(dev->dev, "invalid ioctl: %d (nr=%d)\n", i, nr);
656 return -EINVAL;
657 }
658
659 DBG("register ioctl: %d %08x", nr, plugin->ioctls[i].cmd);
660
661 ioctls[nr] = plugin->ioctls[i];
662
663 if (nr >= omap_drm_driver.num_ioctls) {
664 omap_drm_driver.num_ioctls = nr + 1;
665 }
666 }
667
668 if (loaded) {
669 plugin->load(dev, 0);
670 }
671
672 return 0;
673}
674EXPORT_SYMBOL(omap_drm_register_plugin);
675
676int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin)
677{
678 list_del(&plugin->list);
679 return 0;
680}
681EXPORT_SYMBOL(omap_drm_unregister_plugin);
682
683struct drm_framebuffer * omap_drm_get_default_fb(struct drm_device *dev)
684{
685 struct omap_drm_private *priv = dev->dev_private;
686 return priv->fbdev->fb;
687}
688EXPORT_SYMBOL(omap_drm_get_default_fb);
689
690static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
691{
692 DBG("");
693 return 0;
694}
695
696static int pdev_resume(struct platform_device *device)
697{
698 DBG("");
699 return 0;
700}
701
702static void pdev_shutdown(struct platform_device *device)
703{
704 DBG("");
705}
706
707static int pdev_probe(struct platform_device *device)
708{
709 DBG("%s", device->name);
710 return drm_platform_init(&omap_drm_driver, device);
711}
712
713static int pdev_remove(struct platform_device *device)
714{
715 DBG("");
716 drm_platform_exit(&omap_drm_driver, device);
717 return 0;
718}
719
720struct platform_driver pdev = {
721 .driver = {
722 .name = DRIVER_NAME,
723 .owner = THIS_MODULE,
724 },
725 .probe = pdev_probe,
726 .remove = pdev_remove,
727 .suspend = pdev_suspend,
728 .resume = pdev_resume,
729 .shutdown = pdev_shutdown,
730};
731
732static int __init omap_drm_init(void)
733{
734 DBG("init");
735 return platform_driver_register(&pdev);
736}
737
738static void __exit omap_drm_fini(void)
739{
740 DBG("fini");
741 platform_driver_unregister(&pdev);
742}
743
744/* need late_initcall() so we load after dss_driver's are loaded */
745late_initcall(omap_drm_init);
746module_exit(omap_drm_fini);
747
748MODULE_AUTHOR("Rob Clark <rob@ti.com>");
749MODULE_DESCRIPTION("OMAP DRM Display Driver");
750MODULE_ALIAS("platform:" DRIVER_NAME);
751MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
new file mode 100644
index 00000000000..3a64d5254ea
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_drv.h
@@ -0,0 +1,68 @@
1/*
2 * linux/drivers/staging/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
26#define DBG(fmt,...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
27#define VERB(fmt,...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
28
29#define MODULE_NAME "omapdrm"
30
31struct omap_drm_private {
32 int num_crtcs;
33 struct drm_crtc *crtcs[8];
34 int num_encoders;
35 struct drm_encoder *encoders[8];
36 int num_connectors;
37 struct drm_connector *connectors[8];
38
39 struct drm_fb_helper *fbdev;
40};
41
42struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev);
43
44struct drm_crtc * omap_crtc_init(struct drm_device *dev,
45 struct omap_overlay *ovl, int id);
46struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc);
47
48struct drm_encoder * omap_encoder_init(struct drm_device *dev,
49 struct omap_overlay_manager *mgr);
50struct omap_overlay_manager * omap_encoder_get_manager(
51 struct drm_encoder *encoder);
52struct drm_encoder * omap_connector_attached_encoder (
53 struct drm_connector *connector);
54enum drm_connector_status omap_connector_detect(
55 struct drm_connector *connector, bool force);
56
57struct drm_connector * omap_connector_init(struct drm_device *dev,
58 int connector_type, struct omap_dss_device *dssdev);
59void omap_connector_mode_set(struct drm_connector *connector,
60 struct drm_display_mode *mode);
61void omap_connector_flush(struct drm_connector *connector,
62 int x, int y, int w, int h);
63void omap_connector_dpms(struct drm_connector *connector, int mode);
64
65struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
66 struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd);
67
68#endif /* __OMAP_DRV_H__ */
diff --git a/drivers/staging/omapdrm/omap_encoder.c b/drivers/staging/omapdrm/omap_encoder.c
new file mode 100644
index 00000000000..810c7e244fa
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_encoder.c
@@ -0,0 +1,198 @@
1/*
2 * linux/drivers/staging/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 <linux/omap_drm.h>
21#include "omap_drv.h"
22
23#include "drm_crtc.h"
24#include "drm_crtc_helper.h"
25
26/*
27 * encoder funcs
28 */
29
30#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
31
32struct omap_encoder {
33 struct drm_encoder base;
34 struct omap_overlay_manager *mgr;
35};
36
37static void omap_encoder_destroy(struct drm_encoder *encoder)
38{
39 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
40 DBG("%s", omap_encoder->mgr->name);
41 drm_encoder_cleanup(encoder);
42 kfree(omap_encoder);
43}
44
45static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
46{
47 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
48 struct drm_device *dev = encoder->dev;
49 struct omap_drm_private *priv = dev->dev_private;
50 int i;
51
52 DBG("%s: %d", omap_encoder->mgr->name, mode);
53
54 /* managers don't need to do anything for DPMS.. but we do
55 * need to propagate to the connector, who is actually going
56 * to enable/disable as needed:
57 */
58 for (i = 0; i < priv->num_connectors; i++) {
59 struct drm_connector *connector = priv->connectors[i];
60 if (connector->encoder == encoder) {
61 omap_connector_dpms(connector, mode);
62 }
63 }
64}
65
66static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
67 struct drm_display_mode *mode,
68 struct drm_display_mode *adjusted_mode)
69{
70 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
71 DBG("%s", omap_encoder->mgr->name);
72 return true;
73}
74
75static void omap_encoder_mode_set(struct drm_encoder *encoder,
76 struct drm_display_mode *mode,
77 struct drm_display_mode *adjusted_mode)
78{
79 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
80 struct drm_device *dev = encoder->dev;
81 struct omap_drm_private *priv = dev->dev_private;
82 int i;
83
84 mode = adjusted_mode;
85
86 DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
87 mode->hdisplay, mode->vdisplay);
88
89 for (i = 0; i < priv->num_connectors; i++) {
90 struct drm_connector *connector = priv->connectors[i];
91 if (connector->encoder == encoder) {
92 omap_connector_mode_set(connector, mode);
93 }
94 }
95}
96
97static void omap_encoder_prepare(struct drm_encoder *encoder)
98{
99 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
100 struct drm_encoder_helper_funcs *encoder_funcs =
101 encoder->helper_private;
102 DBG("%s", omap_encoder->mgr->name);
103 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
104}
105
106static void omap_encoder_commit(struct drm_encoder *encoder)
107{
108 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
109 struct drm_encoder_helper_funcs *encoder_funcs =
110 encoder->helper_private;
111 DBG("%s", omap_encoder->mgr->name);
112 omap_encoder->mgr->apply(omap_encoder->mgr);
113 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
114}
115
116static const struct drm_encoder_funcs omap_encoder_funcs = {
117 .destroy = omap_encoder_destroy,
118};
119
120static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
121 .dpms = omap_encoder_dpms,
122 .mode_fixup = omap_encoder_mode_fixup,
123 .mode_set = omap_encoder_mode_set,
124 .prepare = omap_encoder_prepare,
125 .commit = omap_encoder_commit,
126};
127
128struct omap_overlay_manager * omap_encoder_get_manager(
129 struct drm_encoder *encoder)
130{
131 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
132 return omap_encoder->mgr;
133}
134
135/* maybe this could go away and we just use drm_vblank_wait()? */
136int omap_encoder_wait_for_vsync(struct drm_encoder *encoder)
137{
138 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
139 DBG("%s", omap_encoder->mgr->name);
140 return omap_encoder->mgr->wait_for_vsync(omap_encoder->mgr);
141}
142EXPORT_SYMBOL(omap_encoder_wait_for_vsync);
143
144/* initialize encoder */
145struct drm_encoder * omap_encoder_init(struct drm_device *dev,
146 struct omap_overlay_manager *mgr)
147{
148 struct drm_encoder *encoder = NULL;
149 struct omap_encoder *omap_encoder;
150 struct omap_overlay_manager_info info;
151 int ret;
152
153 DBG("%s", mgr->name);
154
155 omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
156 if (!omap_encoder) {
157 dev_err(dev->dev, "could not allocate encoder\n");
158 goto fail;
159 }
160
161 omap_encoder->mgr = mgr;
162 encoder = &omap_encoder->base;
163
164 drm_encoder_init(dev, encoder, &omap_encoder_funcs,
165 DRM_MODE_ENCODER_TMDS);
166 drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
167
168 mgr->get_manager_info(mgr, &info);
169
170 /* TODO: fix hard-coded setup.. */
171 info.default_color = 0x00000000;
172 info.trans_key = 0x00000000;
173 info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
174 info.trans_enabled = false;
175 info.alpha_enabled = true;
176
177 ret = mgr->set_manager_info(mgr, &info);
178 if (ret) {
179 dev_err(dev->dev, "could not set manager info\n");
180 goto fail;
181 }
182
183 ret = mgr->apply(mgr);
184 if (ret) {
185 dev_err(dev->dev, "could not apply\n");
186 goto fail;
187 }
188
189 return encoder;
190
191fail:
192 if (encoder) {
193 drm_encoder_cleanup(encoder);
194 kfree(omap_encoder);
195 }
196
197 return NULL;
198}
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c
new file mode 100644
index 00000000000..c93ec2e9e69
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -0,0 +1,251 @@
1/*
2 * linux/drivers/staging/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 <plat/vram.h>
21
22#include <linux/omap_drm.h>
23#include "omap_drv.h"
24
25#include "drm_crtc.h"
26#include "drm_crtc_helper.h"
27
28
29static char *def_vram;
30module_param_named(vram, def_vram, charp, 0);
31
32/*
33 * framebuffer funcs
34 */
35
36#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
37
38struct omap_framebuffer {
39 struct drm_framebuffer base;
40
41 /* framebuffer size/phys-addr/virt-addr */
42 int size;
43 unsigned long paddr;
44 void __iomem *vaddr;
45};
46
47static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
48 struct drm_file *file_priv,
49 unsigned int *handle)
50{
51 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
52 DBG("framebuffer: get handle: %p", omap_fb);
53
54 // TODO, I suppose this really should be some sort of GEM handle
55 // to the framebuffer object, in case it needs to be mapped or
56 // something. Right now this will go-exist badly with PVR, who
57 // implements the mmap() fxn.. need to think about how to handle
58 // this..
59
60 *handle = 42;
61
62 return 0;
63}
64
65static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
66{
67 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
68
69 DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
70
71 drm_framebuffer_cleanup(fb);
72
73 if (omap_fb->vaddr) {
74 iounmap(omap_fb->vaddr);
75 }
76
77 /* drm framework should, but doesn't (as of 2.6.35) disconnect any
78 * CRTCs connected to this fb before destroying it.. so could be
79 * some small window when garbage is seen on screen. But in
80 * practice, unlikely because we have a private vram pool. So I
81 * won't worry too much about it.
82 */
83 if (omap_fb->paddr) {
84 omap_vram_free(omap_fb->paddr, omap_fb->size);
85 }
86
87 kfree(omap_fb);
88}
89
90static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
91 struct drm_file *file_priv, unsigned flags, unsigned color,
92 struct drm_clip_rect *clips, unsigned num_clips)
93{
94 int i;
95
96 for (i = 0; i < num_clips; i++) {
97 omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
98 clips[i].x2 - clips[i].x1,
99 clips[i].y2 - clips[i].y1);
100 }
101
102 return 0;
103}
104
105static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
106 .create_handle = omap_framebuffer_create_handle,
107 .destroy = omap_framebuffer_destroy,
108 .dirty = omap_framebuffer_dirty,
109};
110
111int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
112 void **vaddr, unsigned long *paddr, int *screen_width)
113{
114 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
115 int bpp = 4; //XXX fb->depth / 8;
116 unsigned long offset;
117
118 offset = (x * bpp) + (y * fb->pitch);
119
120 *vaddr = omap_fb->vaddr + offset;
121 *paddr = omap_fb->paddr + offset;
122 *screen_width = fb->pitch / bpp;
123
124 return omap_fb->size;
125}
126EXPORT_SYMBOL(omap_framebuffer_get_buffer);
127
128/* iterate thru all the connectors, returning ones that are attached
129 * to the same fb..
130 */
131struct drm_connector * omap_framebuffer_get_next_connector(
132 struct drm_framebuffer *fb, struct drm_connector *from)
133{
134 struct drm_device *dev = fb->dev;
135 struct list_head *connector_list = &dev->mode_config.connector_list;
136 struct drm_connector *connector = from;
137
138 if (!from) {
139 return list_first_entry(connector_list, typeof(*from), head);
140 }
141
142 list_for_each_entry_from(connector, connector_list, head) {
143 if (connector != from) {
144 struct drm_encoder *encoder = connector->encoder;
145 struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
146 if (crtc && crtc->fb == fb) {
147 return connector;
148 }
149 }
150 }
151
152 return NULL;
153}
154EXPORT_SYMBOL(omap_framebuffer_get_next_connector);
155
156/* flush an area of the framebuffer (in case of manual update display that
157 * is not automatically flushed)
158 */
159void omap_framebuffer_flush(struct drm_framebuffer *fb,
160 int x, int y, int w, int h)
161{
162 struct drm_connector *connector = NULL;
163
164 VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
165
166 while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
167 /* only consider connectors that are part of a chain */
168 if (connector->encoder && connector->encoder->crtc) {
169 /* TODO: maybe this should propagate thru the crtc who
170 * could do the coordinate translation..
171 */
172 struct drm_crtc *crtc = connector->encoder->crtc;
173 int cx = max(0, x - crtc->x);
174 int cy = max(0, y - crtc->y);
175 int cw = w + (x - crtc->x) - cx;
176 int ch = h + (y - crtc->y) - cy;
177
178 omap_connector_flush(connector, cx, cy, cw, ch);
179 }
180 }
181}
182EXPORT_SYMBOL(omap_framebuffer_flush);
183
184struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
185 struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd)
186{
187 return omap_framebuffer_init(dev, mode_cmd);
188}
189
190struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
191 struct drm_mode_fb_cmd *mode_cmd)
192{
193 struct omap_framebuffer *omap_fb;
194 struct drm_framebuffer *fb = NULL;
195 unsigned long paddr;
196 int size, ret;
197
198 /* in case someone tries to feed us a completely bogus stride: */
199 mode_cmd->pitch = max(mode_cmd->pitch,
200 mode_cmd->width * mode_cmd->bpp / 8);
201
202 /* pvr needs to have a stride that is a multiple of 8 pixels: */
203 mode_cmd->pitch = ALIGN(mode_cmd->pitch, 8 * (mode_cmd->bpp / 8));
204
205 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d)", dev,
206 mode_cmd, mode_cmd->width, mode_cmd->height);
207
208 omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
209 if (!omap_fb) {
210 dev_err(dev->dev, "could not allocate fb\n");
211 goto fail;
212 }
213
214 fb = &omap_fb->base;
215 ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
216 if (ret) {
217 dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
218 goto fail;
219 }
220
221 DBG("create: FB ID: %d (%p)", fb->base.id, fb);
222
223 size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height);
224
225 DBG("allocating %d bytes for fb %d", size, dev->primary->index);
226 ret = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr);
227 if (ret) {
228 dev_err(dev->dev, "failed to allocate vram\n");
229 goto fail;
230 }
231
232 omap_fb->size = size;
233 omap_fb->paddr = paddr;
234 omap_fb->vaddr = ioremap_wc(paddr, size);
235
236 if (!omap_fb->vaddr) {
237 dev_err(dev->dev, "failed to ioremap framebuffer\n");
238 goto fail;
239 }
240
241 drm_helper_mode_fill_fb_struct(fb, mode_cmd);
242
243 return fb;
244
245fail:
246 if (fb) {
247 omap_framebuffer_destroy(fb);
248 }
249 return NULL;
250}
251EXPORT_SYMBOL(omap_framebuffer_init);
diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c
new file mode 100644
index 00000000000..205bc32b914
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_fbdev.c
@@ -0,0 +1,279 @@
1/*
2 * linux/drivers/staging/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 <linux/omap_drm.h>
21#include "omap_drv.h"
22
23#include "drm_crtc.h"
24#include "drm_fb_helper.h"
25
26/*
27 * fbdev funcs, to implement legacy fbdev interface on top of drm driver
28 */
29
30#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
31
32struct omap_fbdev {
33 struct drm_fb_helper base;
34 struct drm_framebuffer *fb;
35};
36
37static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
38
39static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
40 size_t count, loff_t *ppos)
41{
42 ssize_t res;
43
44 res = fb_sys_write(fbi, buf, count, ppos);
45 omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
46
47 return res;
48}
49
50static void omap_fbdev_fillrect(struct fb_info *fbi,
51 const struct fb_fillrect *rect)
52{
53 sys_fillrect(fbi, rect);
54 omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
55}
56
57static void omap_fbdev_copyarea(struct fb_info *fbi,
58 const struct fb_copyarea *area)
59{
60 sys_copyarea(fbi, area);
61 omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
62}
63
64static void omap_fbdev_imageblit(struct fb_info *fbi,
65 const struct fb_image *image)
66{
67 sys_imageblit(fbi, image);
68 omap_fbdev_flush(fbi, image->dx, image->dy,
69 image->width, image->height);
70}
71
72static struct fb_ops omap_fb_ops = {
73 .owner = THIS_MODULE,
74
75 /* Note: to properly handle manual update displays, we wrap the
76 * basic fbdev ops which write to the framebuffer
77 */
78 .fb_read = fb_sys_read,
79 .fb_write = omap_fbdev_write,
80 .fb_fillrect = omap_fbdev_fillrect,
81 .fb_copyarea = omap_fbdev_copyarea,
82 .fb_imageblit = omap_fbdev_imageblit,
83
84 .fb_check_var = drm_fb_helper_check_var,
85 .fb_set_par = drm_fb_helper_set_par,
86 .fb_pan_display = drm_fb_helper_pan_display,
87 .fb_blank = drm_fb_helper_blank,
88 .fb_setcmap = drm_fb_helper_setcmap,
89
90 .fb_debug_enter = drm_fb_helper_debug_enter,
91 .fb_debug_leave = drm_fb_helper_debug_leave,
92};
93
94static int omap_fbdev_create(struct drm_fb_helper *helper,
95 struct drm_fb_helper_surface_size *sizes)
96{
97 struct omap_fbdev *fbdev = to_omap_fbdev(helper);
98 struct drm_device *dev = helper->dev;
99 struct drm_framebuffer *fb;
100 struct fb_info *fbi;
101 struct drm_mode_fb_cmd mode_cmd = {0};
102 unsigned long paddr;
103 void __iomem *vaddr;
104 int size, screen_width;
105 int ret;
106
107 /* only doing ARGB32 since this is what is needed to alpha-blend
108 * with video overlays:
109 */
110 sizes->surface_bpp = 32;
111 sizes->surface_depth = 32;
112
113 DBG("create fbdev: %dx%d@%d", sizes->surface_width,
114 sizes->surface_height, sizes->surface_bpp);
115
116 mode_cmd.width = sizes->surface_width;
117 mode_cmd.height = sizes->surface_height;
118
119 mode_cmd.bpp = sizes->surface_bpp;
120 mode_cmd.depth = sizes->surface_depth;
121
122 mutex_lock(&dev->struct_mutex);
123
124 fbi = framebuffer_alloc(0, dev->dev);
125 if (!fbi) {
126 dev_err(dev->dev, "failed to allocate fb info\n");
127 ret = -ENOMEM;
128 goto fail;
129 }
130
131 DBG("fbi=%p, dev=%p", fbi, dev);
132
133 fbdev->fb = omap_framebuffer_init(dev, &mode_cmd);
134 if (!fbdev->fb) {
135 dev_err(dev->dev, "failed to allocate fb\n");
136 ret = -ENOMEM;
137 goto fail;
138 }
139
140 fb = fbdev->fb;
141 helper->fb = fb;
142 helper->fbdev = fbi;
143
144 fbi->par = helper;
145 fbi->flags = FBINFO_DEFAULT;
146 fbi->fbops = &omap_fb_ops;
147
148 strcpy(fbi->fix.id, MODULE_NAME);
149
150 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
151 if (ret) {
152 ret = -ENOMEM;
153 goto fail;
154 }
155
156 drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
157 drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
158
159 size = omap_framebuffer_get_buffer(fb, 0, 0,
160 &vaddr, &paddr, &screen_width);
161
162 dev->mode_config.fb_base = paddr;
163
164 fbi->screen_base = vaddr;
165 fbi->screen_size = size;
166 fbi->fix.smem_start = paddr;
167 fbi->fix.smem_len = size;
168
169 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
170 DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
171
172 mutex_unlock(&dev->struct_mutex);
173
174 return 0;
175
176fail:
177 mutex_unlock(&dev->struct_mutex);
178 // TODO cleanup?
179 return ret;
180}
181
182static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
183 u16 red, u16 green, u16 blue, int regno)
184{
185 DBG("fbdev: set gamma");
186 // XXX ignore?
187}
188
189static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
190 u16 *red, u16 *green, u16 *blue, int regno)
191{
192 DBG("fbdev: get gamma");
193 // XXX ignore?
194}
195
196static int omap_fbdev_probe(struct drm_fb_helper *helper,
197 struct drm_fb_helper_surface_size *sizes)
198{
199 int new_fb = 0;
200 int ret;
201
202 if (!helper->fb) {
203 ret = omap_fbdev_create(helper, sizes);
204 if (ret)
205 return ret;
206 new_fb = 1;
207 }
208 return new_fb;
209}
210
211static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
212 .gamma_set = omap_crtc_fb_gamma_set,
213 .gamma_get = omap_crtc_fb_gamma_get,
214 .fb_probe = omap_fbdev_probe,
215};
216
217static struct drm_fb_helper * get_fb(struct fb_info *fbi)
218{
219 if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
220 /* these are not the fb's you're looking for */
221 return NULL;
222 }
223 return fbi->par;
224}
225
226/* flush an area of the framebuffer (in case of manual update display that
227 * is not automatically flushed)
228 */
229static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
230{
231 struct drm_fb_helper *helper = get_fb(fbi);
232
233 if (!helper)
234 return;
235
236 VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
237
238 omap_framebuffer_flush(helper->fb, x, y, w, h);
239}
240EXPORT_SYMBOL(omap_fbdev_flush);
241
242/* initialize fbdev helper */
243struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev)
244{
245 struct omap_drm_private *priv = dev->dev_private;
246 struct omap_fbdev *fbdev = NULL;
247 struct drm_fb_helper *helper;
248 int ret = 0;
249
250 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
251 if (!fbdev) {
252 dev_err(dev->dev, "could not allocate fbdev\n");
253 goto fail;
254 }
255
256 helper = &fbdev->base;
257
258 helper->funcs = &omap_fb_helper_funcs;
259
260 ret = drm_fb_helper_init(dev, helper,
261 priv->num_crtcs, priv->num_connectors);
262 if (ret) {
263 dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
264 goto fail;
265 }
266
267 drm_fb_helper_single_add_all_connectors(helper);
268 drm_fb_helper_initial_config(helper, 32);
269
270 priv->fbdev = helper;
271
272 return helper;
273
274fail:
275 if (fbdev) {
276 kfree(fbdev);
277 }
278 return NULL;
279}