summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorRob Clark <rob@ti.com>2011-04-03 22:05:25 -0300
committerAndy Doan <doanac@gmail.com>2011-04-13 15:32:07 -0700
commitd795e2ce653e6c0e622c9b31f91f0c525c5982cb (patch)
tree6b08f85c0505bfda8e2fbfcdbb6c8c87fe879c7c /drivers/gpu
parentcf42e0d832c909f71e58d81238e082791cff1dca (diff)
Adding omap_gpu 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. omap_gpu 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. To set vram on the command-line (bootargs): omapgpu.vram=0:32M (same syntax as omapfb but use omapgpu module name instead) To set default resolution on command-line, use the normal video= line, such as: video="HDMI Type A-1:800x600@60" And of course, to enable debug: drm.debug=7 The omap_gpu drm driver supports using platform_data to configure which omap_gpu devices are assigned which overlays/managers/devices. If multiple devices are registered, each one creates it's own framebuffer and fbdev. This allows for multiple independent display contexts (like with the old omapfb driver) in cases where you don't want to share one virtual framebuffer across multiple displays. Signed-off-by: Rob Clark <rob@ti.com> Signed-off-by: Ricardo Salveti de Araujo <ricardo.salveti@canonical.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig24
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/omap/Makefile8
-rw-r--r--drivers/gpu/drm/omap/omap_connector.c397
-rw-r--r--drivers/gpu/drm/omap/omap_crtc.c277
-rw-r--r--drivers/gpu/drm/omap/omap_encoder.c198
-rw-r--r--drivers/gpu/drm/omap/omap_fb.c368
-rw-r--r--drivers/gpu/drm/omap/omap_fbdev.c298
-rw-r--r--drivers/gpu/drm/omap/omap_gpu.c751
-rw-r--r--drivers/gpu/drm/omap/omap_gpu_priv.h80
10 files changed, 2402 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 0902d446003..9c103b2e2f4 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -165,3 +165,27 @@ config DRM_SAVAGE
help
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage.
+
+config DRM_OMAP
+ tristate "OMAP GPU (EXPERIMENTAL)"
+ depends on DRM && !CONFIG_FB_OMAP2
+ select DRM_KMS_HELPER
+ select OMAP2_VRAM
+ select OMAP2_DSS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ default y
+ help
+ DRM display driver for OMAP2/3/4 based boards.
+
+config DRM_OMAP_NUM_CRTCS
+ int "Number of CRTCs"
+ range 1 10
+ default 1 if ARCH_OMAP2 || ARCH_OMAP3
+ default 2 if ARCH_OMAP4
+ depends on DRM_OMAP
+ help
+ Select the number of video overlays which can be used as framebuffers.
+ The remaining overlays are reserved for video.
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 997c43d0490..dc1a49620e7 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -36,4 +36,5 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-$(CONFIG_DRM_OMAP) += omap/
obj-y += i2c/
diff --git a/drivers/gpu/drm/omap/Makefile b/drivers/gpu/drm/omap/Makefile
new file mode 100644
index 00000000000..0a2513f563f
--- /dev/null
+++ b/drivers/gpu/drm/omap/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+omapgpu-y := omap_gpu.o omap_crtc.o omap_encoder.o omap_connector.o omap_fb.o omap_fbdev.o
+
+obj-$(CONFIG_DRM_OMAP) += omapgpu.o
diff --git a/drivers/gpu/drm/omap/omap_connector.c b/drivers/gpu/drm/omap/omap_connector.c
new file mode 100644
index 00000000000..2001e0e72da
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_connector.c
@@ -0,0 +1,397 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_connector.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_gpu.h>
+#include "omap_gpu_priv.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * connector funcs
+ */
+
+#define to_omap_connector(x) container_of(x, struct omap_connector, base)
+
+struct omap_connector {
+ struct drm_connector base;
+ struct omap_dss_device *dssdev;
+};
+
+static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
+ struct omap_video_timings *timings)
+{
+ mode->clock = timings->pixel_clock;
+
+ mode->hdisplay = timings->x_res;
+ mode->hsync_start = mode->hdisplay + timings->hfp;
+ mode->hsync_end = mode->hsync_start + timings->hsw;
+ mode->htotal = mode->hsync_end + timings->hbp;
+
+ mode->vdisplay = timings->y_res;
+ mode->vsync_start = mode->vdisplay + timings->vfp;
+ mode->vsync_end = mode->vsync_start + timings->vsw;
+ mode->vtotal = mode->vsync_end + timings->vbp;
+
+ /* note: whether or not it is interlaced, +/- h/vsync, etc,
+ * which should be set in the mode flags, is not exposed in
+ * the omap_video_timings struct.. but hdmi driver tracks
+ * those separately so all we have to have to set the mode
+ * is the way to recover these timings values, and the
+ * omap_dss_driver would do the rest.
+ */
+}
+
+static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
+ struct drm_display_mode *mode)
+{
+ timings->pixel_clock = mode->clock;
+
+ timings->x_res = mode->hdisplay;
+ timings->hfp = mode->hsync_start - mode->hdisplay;
+ timings->hsw = mode->hsync_end - mode->hsync_start;
+ timings->hbp = mode->htotal - mode->hsync_end;
+
+ timings->y_res = mode->vdisplay;
+ timings->vfp = mode->vsync_start - mode->vdisplay;
+ timings->vsw = mode->vsync_end - mode->vsync_start;
+ timings->vbp = mode->vtotal - mode->vsync_end;
+}
+
+void omap_connector_dpms(struct drm_connector *connector, int mode)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+
+ /* TODO: add API in DSS to suspend/resume individual displays.. */
+
+ DBG("%s: %d", dssdev->name, mode);
+}
+
+enum drm_connector_status omap_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+ enum drm_connector_status ret;
+
+ if (dssdrv->is_detected(dssdev)) {
+ ret = connector_status_connected;
+ } else {
+ ret = connector_status_disconnected;
+ }
+
+ DBG("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
+
+ return ret;
+}
+
+static void omap_connector_destroy(struct drm_connector *connector)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+
+ DBG("%s", omap_connector->dssdev->name);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(omap_connector);
+}
+
+#define MAX_EDID 256
+
+static int omap_connector_get_modes(struct drm_connector *connector)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+ struct drm_device *dev = connector->dev;
+ int n = 0;
+
+ DBG("%s", omap_connector->dssdev->name);
+
+ /* if display exposes EDID, then we parse that in the normal way to
+ * build table of supported modes.. otherwise (ie. fixed resolution
+ * LCD panels) we just return a single mode corresponding to the
+ * currently configured timings:
+ */
+ if (dssdrv->get_edid) {
+ void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
+
+ if ((dssdrv->get_edid(dssdev, edid, MAX_EDID) == 0) &&
+ drm_edid_is_valid(edid)) {
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+ kfree(connector->display_info.raw_edid);
+ connector->display_info.raw_edid = edid;
+ } else {
+ drm_mode_connector_update_edid_property(connector, NULL);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+ }
+ } else {
+ struct drm_display_mode *mode = drm_mode_create(dev);
+ struct omap_video_timings timings;
+
+ dssdrv->get_timings(dssdev, &timings);
+
+ copy_timings_omap_to_drm(mode, &timings);
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ n = 1;
+ }
+
+ return n;
+}
+
+static int omap_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+ struct omap_video_timings timings = {0};
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *new_mode;
+ int ret = MODE_BAD;
+
+ copy_timings_drm_to_omap(&timings, mode);
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ if (!dssdrv->check_timings(dssdev, &timings)) {
+ /* check if vrefresh is still valid */
+ new_mode = drm_mode_duplicate(dev, mode);
+ new_mode->clock = timings.pixel_clock;
+ new_mode->vrefresh = 0;
+ if (mode->vrefresh == drm_mode_vrefresh(new_mode))
+ ret = MODE_OK;
+ drm_mode_destroy(dev, new_mode);
+ }
+
+ DBG("connector: mode %s: "
+ "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+ (ret == MODE_OK) ? "valid" : "invalid",
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+
+ return ret;
+}
+
+struct drm_encoder * omap_connector_attached_encoder(
+ struct drm_connector *connector)
+{
+ int i;
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ struct drm_mode_object *obj;
+
+ if (connector->encoder_ids[i] == 0)
+ break;
+
+ obj = drm_mode_object_find(connector->dev,
+ connector->encoder_ids[i],
+ DRM_MODE_OBJECT_ENCODER);
+
+ if (obj) {
+ struct drm_encoder *encoder = obj_to_encoder(obj);
+ struct omap_overlay_manager *mgr =
+ omap_encoder_get_manager(encoder);
+ DBG("%s: found %s", omap_connector->dssdev->name,
+ mgr->name);
+ return encoder;
+ }
+ }
+
+ DBG("%s: no encoder", omap_connector->dssdev->name);
+
+ return NULL;
+}
+
+static const struct drm_connector_funcs omap_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = omap_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = omap_connector_destroy,
+};
+
+static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
+ .get_modes = omap_connector_get_modes,
+ .mode_valid = omap_connector_mode_valid,
+ .best_encoder = omap_connector_attached_encoder,
+};
+
+/* called from encoder when mode is set, to propagate settings to the dssdev */
+void omap_connector_mode_set(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = connector->dev;
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+ struct omap_video_timings timings;
+
+ copy_timings_drm_to_omap(&timings, mode);
+
+ DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+ omap_connector->dssdev->name,
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+
+ if (dssdrv->check_timings(dssdev, &timings)) {
+ dev_err(dev->dev, "could not set timings\n");
+ return;
+ }
+
+ dssdrv->set_timings(dssdev, &timings);
+}
+
+enum omap_dss_update_mode omap_connector_get_update_mode(
+ struct drm_connector *connector)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+
+ DBG("%s", omap_connector->dssdev->name);
+
+ if (dssdrv->get_update_mode) {
+ return dssdrv->get_update_mode(dssdev);
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL(omap_connector_get_update_mode);
+
+int omap_connector_set_update_mode(struct drm_connector *connector,
+ enum omap_dss_update_mode mode)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+
+ DBG("%s: %d", omap_connector->dssdev->name, mode);
+
+ if (dssdrv->set_update_mode) {
+ return dssdrv->set_update_mode(dssdev, mode);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(omap_connector_set_update_mode);
+
+int omap_connector_sync(struct drm_connector *connector)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+ struct omap_dss_device *dssdev = omap_connector->dssdev;
+ struct omap_dss_driver *dssdrv = dssdev->driver;
+
+ DBG("%s", omap_connector->dssdev->name);
+
+ if (dssdrv->sync) {
+ return dssdrv->sync(dssdev);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(omap_connector_sync);
+
+/* flush an area of the framebuffer (in case of manual update display that
+ * is not automatically flushed)
+ */
+void omap_connector_flush(struct drm_connector *connector,
+ int x, int y, int w, int h)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+
+ /* TODO: enable when supported in dss */
+ VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
+}
+
+/* initialize connector */
+struct drm_connector * omap_connector_init(struct drm_device *dev,
+ int connector_type, struct omap_dss_device *dssdev)
+{
+ struct drm_connector *connector = NULL;
+ struct omap_connector *omap_connector;
+
+ DBG("%s", dssdev->name);
+
+ omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
+ if (!omap_connector) {
+ dev_err(dev->dev, "could not allocate connector\n");
+ goto fail;
+ }
+
+ omap_connector->dssdev = dssdev;
+ connector = &omap_connector->base;
+
+ drm_connector_init(dev, connector, &omap_connector_funcs,
+ connector_type);
+ drm_connector_helper_add(connector, &omap_connector_helper_funcs);
+
+ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD) {
+ connector->polled = 0;
+ } else {
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ }
+
+ connector->interlace_allowed = 1;
+ connector->doublescan_allowed = 0;
+
+ drm_sysfs_connector_add(connector);
+
+ /* store resume info for suspended displays */
+ switch (dssdev->state) {
+ case OMAP_DSS_DISPLAY_SUSPENDED:
+ dssdev->activate_after_resume = true;
+ break;
+ case OMAP_DSS_DISPLAY_DISABLED:
+ if (dssdev->driver) {
+ int ret = dssdev->driver->enable(dssdev);
+ if (ret) {
+ DBG("%s: failed to enable: %d",
+ dssdev->name, ret);
+ dssdev->driver->disable(dssdev);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return connector;
+
+fail:
+ if (connector) {
+ drm_connector_cleanup(connector);
+ kfree(omap_connector);
+ }
+
+ return NULL;
+}
diff --git a/drivers/gpu/drm/omap/omap_crtc.c b/drivers/gpu/drm/omap/omap_crtc.c
new file mode 100644
index 00000000000..1726fd76f14
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_crtc.c
@@ -0,0 +1,277 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_crtc.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_gpu.h>
+#include "omap_gpu_priv.h"
+
+#include "drm_mode.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
+
+struct omap_crtc {
+ struct drm_crtc base;
+ struct omap_overlay *ovl;
+ struct omap_overlay_info info;
+};
+
+static int commit(struct drm_crtc *crtc);
+
+/* update parameters that are dependent on the framebuffer dimensions and
+ * position within the fb that this crtc scans out from. This is called
+ * when framebuffer dimensions or x,y base may have changed, either due
+ * to our mode, or a change in another crtc that is scanning out of the
+ * same fb.
+ */
+static void omap_crtc_update_scanout(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ unsigned long paddr;
+ void __iomem *vaddr;
+ int screen_width;
+
+ omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y,
+ &vaddr, &paddr, &screen_width);
+
+ DBG("%s: %d,%d: %p %08x (%d)", omap_crtc->ovl->name,
+ crtc->x, crtc->y, vaddr, paddr, screen_width);
+
+ omap_crtc->info.paddr = paddr;
+ omap_crtc->info.vaddr = vaddr;
+ omap_crtc->info.screen_width = screen_width;
+}
+
+static void omap_crtc_gamma_set(struct drm_crtc *crtc,
+ u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ DBG("%s", omap_crtc->ovl->name);
+ // XXX ignore?
+}
+
+static void omap_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ DBG("%s", omap_crtc->ovl->name);
+ drm_crtc_cleanup(crtc);
+ kfree(omap_crtc);
+}
+
+static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+ DBG("%s: %d", omap_crtc->ovl->name, mode);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ omap_crtc_update_scanout(crtc);
+ omap_crtc->info.enabled = true;
+ } else {
+ omap_crtc->info.enabled = false;
+ }
+
+ commit(crtc);
+}
+
+static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ // XXX I guess we support anything?
+ DBG("%s", omap_crtc->ovl->name);
+ return true;
+}
+
+static int omap_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+ DBG("%s: %d,%d: %dx%d",omap_crtc->ovl->name, x, y,
+ mode->hdisplay, mode->vdisplay);
+
+ /* just use adjusted mode */
+ mode = adjusted_mode;
+
+ omap_crtc->info.width = mode->hdisplay;
+ omap_crtc->info.height = mode->vdisplay;
+ omap_crtc->info.out_width = mode->hdisplay;
+ omap_crtc->info.out_height = mode->vdisplay;
+ omap_crtc->info.color_mode = OMAP_DSS_COLOR_ARGB32;
+ omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA;
+ omap_crtc->info.rotation = OMAP_DSS_ROT_0;
+#if 0 /* enable when supported in dss */
+ omap_crtc->info.zorder = 3; /* GUI in the front, video behind */
+#endif
+ omap_crtc->info.global_alpha = 0xff;
+ omap_crtc->info.mirror = 0;
+ omap_crtc->info.mirror = 0;
+ omap_crtc->info.pos_x = 0;
+ omap_crtc->info.pos_y = 0;
+#if 0 /* enable when supported in dss */
+ omap_crtc->info.min_x_decim = 1;
+ omap_crtc->info.max_x_decim = 1;
+ omap_crtc->info.min_y_decim = 1;
+ omap_crtc->info.max_y_decim = 1;
+#endif
+
+ omap_crtc_update_scanout(crtc);
+
+ return 0;
+}
+
+static void omap_crtc_prepare(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct omap_overlay *ovl = omap_crtc->ovl;
+
+ DBG("%s", omap_crtc->ovl->name);
+
+ ovl->get_overlay_info(ovl, &omap_crtc->info);
+
+ omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static int commit(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct omap_overlay *ovl = omap_crtc->ovl;
+ struct omap_overlay_info *info = &omap_crtc->info;
+ int ret;
+
+ DBG("%s", omap_crtc->ovl->name);
+ DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
+ info->out_height, info->screen_width);
+ DBG("%d,%d %p %08x", info->pos_x, info->pos_y, info->vaddr,
+ info->paddr);
+
+ /* NOTE: do we want to do this at all here, or just wait
+ * for dpms(ON) since other CRTC's may not have their mode
+ * set yet, so fb dimensions may still change..
+ */
+ ret = ovl->set_overlay_info(ovl, info);
+ if (ret) {
+ dev_err(dev->dev, "could not set overlay info\n");
+ return ret;
+ }
+
+ /* our encoder doesn't necessarily get a commit() after this, in
+ * particular in the dpms() and mode_set_base() cases, so force the
+ * manager to update:
+ *
+ * could this be in the encoder somehow?
+ */
+ if (ovl->manager) {
+ ret = ovl->manager->apply(ovl->manager);
+ if (ret) {
+ dev_err(dev->dev, "could not apply\n");
+ return ret;
+ }
+ }
+
+ if (info->enabled) {
+ omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y,
+ crtc->fb->width, crtc->fb->height);
+ }
+
+ return 0;
+}
+
+static void omap_crtc_commit(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ DBG("%s", omap_crtc->ovl->name);
+ omap_crtc_dpms (crtc, DRM_MODE_DPMS_ON);
+}
+
+static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+ DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb);
+
+ omap_crtc_update_scanout(crtc);
+
+ return commit(crtc);
+}
+
+void omap_crtc_load_lut(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ DBG("%s", omap_crtc->ovl->name);
+}
+
+static const struct drm_crtc_funcs omap_crtc_funcs = {
+ .gamma_set = omap_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = omap_crtc_destroy,
+//TODO .page_flip = omap_page_flip,
+};
+
+static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
+ .dpms = omap_crtc_dpms,
+ .mode_fixup = omap_crtc_mode_fixup,
+ .mode_set = omap_crtc_mode_set,
+ .prepare = omap_crtc_prepare,
+ .commit = omap_crtc_commit,
+ .mode_set_base = omap_crtc_mode_set_base,
+ .load_lut = omap_crtc_load_lut,
+};
+
+struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ return omap_crtc->ovl;
+}
+
+/* initialize crtc */
+struct drm_crtc * omap_crtc_init(struct drm_device *dev,
+ struct omap_overlay *ovl)
+{
+ struct drm_crtc *crtc = NULL;
+ struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
+
+ DBG("%s", ovl->name);
+
+ if (!omap_crtc) {
+ dev_err(dev->dev, "could not allocate CRTC\n");
+ goto fail;
+ }
+
+ omap_crtc->ovl = ovl;
+ crtc = &omap_crtc->base;
+ drm_crtc_init(dev, crtc, &omap_crtc_funcs);
+ drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
+
+ return crtc;
+
+fail:
+ if (crtc) {
+ drm_crtc_cleanup(crtc);
+ kfree(omap_crtc);
+ }
+ return NULL;
+}
diff --git a/drivers/gpu/drm/omap/omap_encoder.c b/drivers/gpu/drm/omap/omap_encoder.c
new file mode 100644
index 00000000000..e60fe81073b
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_encoder.c
@@ -0,0 +1,198 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_encoder.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_gpu.h>
+#include "omap_gpu_priv.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * encoder funcs
+ */
+
+#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
+
+struct omap_encoder {
+ struct drm_encoder base;
+ struct omap_overlay_manager *mgr;
+};
+
+static void omap_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ DBG("%s", omap_encoder->mgr->name);
+ drm_encoder_cleanup(encoder);
+ kfree(omap_encoder);
+}
+
+static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct omap_gpu_private *priv = dev->dev_private;
+ int i;
+
+ DBG("%s: %d", omap_encoder->mgr->name, mode);
+
+ /* managers don't need to do anything for DPMS.. but we do
+ * need to propagate to the connector, who is actually going
+ * to enable/disable as needed:
+ */
+ for (i = 0; i < priv->num_connectors; i++) {
+ struct drm_connector *connector = priv->connectors[i];
+ if (connector->encoder == encoder) {
+ omap_connector_dpms(connector, mode);
+ }
+ }
+}
+
+static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ DBG("%s", omap_encoder->mgr->name);
+ return true;
+}
+
+static void omap_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct omap_gpu_private *priv = dev->dev_private;
+ int i;
+
+ mode = adjusted_mode;
+
+ DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
+ mode->hdisplay, mode->vdisplay);
+
+ for (i = 0; i < priv->num_connectors; i++) {
+ struct drm_connector *connector = priv->connectors[i];
+ if (connector->encoder == encoder) {
+ omap_connector_mode_set(connector, mode);
+ }
+ }
+}
+
+static void omap_encoder_prepare(struct drm_encoder *encoder)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ struct drm_encoder_helper_funcs *encoder_funcs =
+ encoder->helper_private;
+ DBG("%s", omap_encoder->mgr->name);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_encoder_commit(struct drm_encoder *encoder)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ struct drm_encoder_helper_funcs *encoder_funcs =
+ encoder->helper_private;
+ DBG("%s", omap_encoder->mgr->name);
+ omap_encoder->mgr->apply(omap_encoder->mgr);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static const struct drm_encoder_funcs omap_encoder_funcs = {
+ .destroy = omap_encoder_destroy,
+};
+
+static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
+ .dpms = omap_encoder_dpms,
+ .mode_fixup = omap_encoder_mode_fixup,
+ .mode_set = omap_encoder_mode_set,
+ .prepare = omap_encoder_prepare,
+ .commit = omap_encoder_commit,
+};
+
+struct omap_overlay_manager * omap_encoder_get_manager(
+ struct drm_encoder *encoder)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ return omap_encoder->mgr;
+}
+
+/* maybe this could go away and we just use drm_vblank_wait()? */
+int omap_encoder_wait_for_vsync(struct drm_encoder *encoder)
+{
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ DBG("%s", omap_encoder->mgr->name);
+ return omap_encoder->mgr->wait_for_vsync(omap_encoder->mgr);
+}
+EXPORT_SYMBOL(omap_encoder_wait_for_vsync);
+
+/* initialize encoder */
+struct drm_encoder * omap_encoder_init(struct drm_device *dev,
+ struct omap_overlay_manager *mgr)
+{
+ struct drm_encoder *encoder = NULL;
+ struct omap_encoder *omap_encoder;
+ struct omap_overlay_manager_info info;
+ int ret;
+
+ DBG("%s", mgr->name);
+
+ omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
+ if (!omap_encoder) {
+ dev_err(dev->dev, "could not allocate encoder\n");
+ goto fail;
+ }
+
+ omap_encoder->mgr = mgr;
+ encoder = &omap_encoder->base;
+
+ mgr->get_manager_info(mgr, &info);
+
+ /* TODO: fix hard-coded setup.. */
+ info.default_color = 0x00000000;
+ info.trans_key = 0x00000000;
+ info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+ info.trans_enabled = false;
+ info.alpha_enabled = false;
+
+ ret = mgr->set_manager_info(mgr, &info);
+ if (ret) {
+ dev_err(dev->dev, "could not set manager info\n");
+ goto fail;
+ }
+
+ ret = mgr->apply(mgr);
+ if (ret) {
+ dev_err(dev->dev, "could not apply\n");
+ goto fail;
+ }
+
+ drm_encoder_init(dev, encoder, &omap_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
+
+ return encoder;
+
+fail:
+ if (encoder) {
+ drm_encoder_cleanup(encoder);
+ kfree(omap_encoder);
+ }
+
+ return NULL;
+}
diff --git a/drivers/gpu/drm/omap/omap_fb.c b/drivers/gpu/drm/omap/omap_fb.c
new file mode 100644
index 00000000000..b3ac7fb21ac
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_fb.c
@@ -0,0 +1,368 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_fb.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <plat/vram.h>
+
+#include <linux/omap_gpu.h>
+#include "omap_gpu_priv.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+
+static char *def_vram;
+module_param_named(vram, def_vram, charp, 0);
+
+/*
+ * framebuffer funcs
+ */
+
+#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
+
+struct omap_framebuffer {
+ struct drm_framebuffer base;
+
+ /* framebuffer size/phys-addr/virt-addr */
+ int size;
+ unsigned long paddr;
+ void __iomem *vaddr;
+};
+
+
+/* copied from omapfb-main.c to preserve vram param syntax */
+static int parse_vram_param(const char *param, int max_entries,
+ unsigned long *sizes, unsigned long *paddrs)
+{
+ int fbnum;
+ unsigned long size;
+ unsigned long paddr = 0;
+ char *p, *start;
+
+ DBG("vram: %s", param);
+
+ start = (char *)param;
+
+ while (1) {
+ p = start;
+
+ fbnum = simple_strtoul(p, &p, 10);
+
+ if ((p == param) || (*p != ':') || (fbnum >= max_entries))
+ return -EINVAL;
+
+ size = memparse(p + 1, &p);
+
+ if (!size)
+ return -EINVAL;
+
+ paddr = 0;
+
+ if (*p == '@') {
+ paddr = simple_strtoul(p + 1, &p, 16);
+
+ if (!paddr)
+ return -EINVAL;
+ }
+
+ paddrs[fbnum] = paddr;
+ sizes[fbnum] = size;
+
+ if (*p == 0)
+ break;
+
+ if (*p != ',')
+ return -EINVAL;
+
+ ++p;
+
+ start = p;
+ }
+
+ return 0;
+}
+
+#define MAX_FBS 10
+
+static int allocate_vram(struct drm_framebuffer *fb, int idx, int size)
+{
+ struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ struct drm_device *dev = fb->dev;
+ unsigned long sizes[MAX_FBS] = {0};
+ unsigned long paddrs[MAX_FBS] = {0};
+ unsigned long paddr = 0;
+ int ret = ret;
+
+ if (idx >= MAX_FBS) {
+ dev_err(dev->dev, "invalid fb number: %d\n", idx);
+ goto fail;
+ }
+
+ if (def_vram) {
+ if (parse_vram_param(def_vram, MAX_FBS, sizes, paddrs)) {
+ dev_err(dev->dev, "failed to parse vram parameter\n");
+ memset(&sizes, 0, sizeof(sizes));
+ memset(&paddrs, 0, sizeof(paddrs));
+ } else {
+ size = sizes[idx];
+ paddr = paddrs[idx];
+ }
+ }
+
+ size = PAGE_ALIGN(size);
+
+ if (paddr) {
+ DBG("reserving %d bytes at %lx for fb %d", size, paddr, idx);
+ ret = omap_vram_reserve(paddr, size);
+ } else {
+ DBG("allocating %d bytes for fb %d", size, idx);
+ ret = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr);
+ }
+
+ if (ret) {
+ dev_err(dev->dev, "failed to allocate vram\n");
+ goto fail;
+ }
+
+ omap_fb->size = size;
+ omap_fb->paddr = paddr;
+ omap_fb->vaddr = ioremap_wc(paddr, size);
+
+ if (!omap_fb->vaddr) {
+ dev_err(dev->dev, "failed to ioremap framebuffer\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ //memset(vaddr, 0, size);
+
+ return 0;
+
+fail:
+ if (omap_fb->paddr) {
+ omap_vram_free(paddr, size);
+ }
+
+ omap_fb->size = 0;
+ omap_fb->vaddr = NULL;
+ omap_fb->paddr = 0;
+
+ return ret;
+}
+
+static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ DBG("framebuffer: get handle: %p", omap_fb);
+
+ // TODO, I suppose this really should be some sort of GEM handle
+ // to the framebuffer object, in case it needs to be mapped or
+ // something. Right now this will go-exist badly with PVR, who
+ // implements the mmap() fxn.. need to think about how to handle
+ // this..
+
+ *handle = 42;
+
+ return 0;
+}
+
+static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ /* omap_vram_free() doesn't really do what you'd think.. (or at
+ * least not if you think it'd return vram to the pool), so
+ * disabling this for now until there is a better way to alloc
+ * and free coherant..
+ */
+ DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
+}
+
+static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv, unsigned flags, unsigned color,
+ struct drm_clip_rect *clips, unsigned num_clips)
+{
+ int i;
+
+ for (i = 0; i < num_clips; i++) {
+ omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
+ clips[i].x2 - clips[i].x1,
+ clips[i].y2 - clips[i].y1);
+ }
+
+ return 0;
+}
+
+static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
+ .create_handle = omap_framebuffer_create_handle,
+ .destroy = omap_framebuffer_destroy,
+ .dirty = omap_framebuffer_dirty,
+};
+
+int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
+ void **vaddr, unsigned long *paddr, int *screen_width)
+{
+ struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ int bpp = 4; //XXX fb->depth / 8;
+ unsigned long offset;
+
+ offset = (x * bpp) + (y * fb->pitch);
+
+ *vaddr = omap_fb->vaddr + offset;
+ *paddr = omap_fb->paddr + offset;
+ *screen_width = fb->pitch / bpp;
+
+ return omap_fb->size;
+}
+
+/* iterate thru all the connectors, returning ones that are attached
+ * to the same fb..
+ */
+struct drm_connector * omap_framebuffer_get_next_connector(
+ struct drm_framebuffer *fb, struct drm_connector *from)
+{
+ struct drm_device *dev = fb->dev;
+ struct list_head *connector_list = &dev->mode_config.connector_list;
+ struct drm_connector *connector = from;
+
+ if (!from) {
+ return list_first_entry(connector_list, typeof(*from), head);
+ }
+
+ list_for_each_entry_from(connector, connector_list, head) {
+ if (connector != from) {
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
+ if (crtc && crtc->fb == fb) {
+ return connector;
+ }
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(omap_framebuffer_get_next_connector);
+
+/* flush an area of the framebuffer (in case of manual update display that
+ * is not automatically flushed)
+ */
+void omap_framebuffer_flush(struct drm_framebuffer *fb,
+ int x, int y, int w, int h)
+{
+ struct drm_connector *connector = NULL;
+
+ VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
+
+ while ((connector =
+ omap_framebuffer_get_next_connector(fb, connector))) {
+ /* only consider connectors that are part of a chain */
+ if (connector->encoder && connector->encoder->crtc) {
+ /* TODO: maybe this should propagate thru the crtc who
+ * could do the coordinate translation..
+ */
+ struct drm_crtc *crtc = connector->encoder->crtc;
+ int cx = max(0, x - crtc->x);
+ int cy = max(0, y - crtc->y);
+ int cw = w + (x - crtc->x) - cx;
+ int ch = h + (y - crtc->y) - cy;
+
+ omap_connector_flush(connector, cx, cy, cw, ch);
+ }
+ }
+}
+EXPORT_SYMBOL(omap_framebuffer_flush);
+
+struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd)
+{
+ return omap_framebuffer_init(dev, mode_cmd);
+}
+
+struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct omap_gpu_private *priv = dev->dev_private;
+ struct omap_framebuffer *omap_fb = NULL;
+ struct drm_framebuffer *fb;
+ int ret;
+
+ /* in case someone tries to feed us a completely bogus stride: */
+ mode_cmd->pitch = max(mode_cmd->pitch,
+ mode_cmd->width * mode_cmd->bpp / 8);
+
+ /* pvr needs to have a stride that is a multiple of 8 pixels: */
+ mode_cmd->pitch = ALIGN(mode_cmd->pitch, 8 * (mode_cmd->bpp / 8));
+
+ /* for now, we can only support a single fb.. so we need to resize
+ * the old one instead of creating a new one of different size. If
+ * we can eventually get rid of the vram pool and had a better way
+ * to allocate contiguous memory after boot time, this restriction
+ * should be lifted.
+ */
+ if (priv->fb) {
+ fb = priv->fb;
+ DBG("recycle: FB ID: %d (%p)", fb->base.id, fb);
+ goto recycle; /* ugg, we need CMA! */
+ }
+
+ DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d)", dev,
+ mode_cmd, mode_cmd->width, mode_cmd->height);
+
+ omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
+ if (!omap_fb) {
+ dev_err(dev->dev, "could not allocate fb\n");
+ goto fail;
+ }
+
+ fb = &omap_fb->base;
+ ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
+ if (ret) {
+ dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
+ goto fail;
+ }
+
+ DBG("create: FB ID: %d (%p)", fb->base.id, fb);
+
+ ret = allocate_vram(fb, dev->primary->index,
+ mode_cmd->pitch * mode_cmd->height);
+ if (ret) {
+ dev_err(dev->dev, "failed to allocate framebuffer\n");
+ goto fail;
+ }
+
+recycle:
+ drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+ priv->fb = fb;
+
+ if (priv->fbdev) {
+ /* if fbdev is already created, we need to update it to
+ * be attached to the new fb
+ */
+ omap_fbdev_update(priv->fbdev, fb);
+ }
+
+ return fb;
+
+fail:
+ if (omap_fb) {
+ kfree(omap_fb);
+ }
+ return NULL;
+}
+
diff --git a/drivers/gpu/drm/omap/omap_fbdev.c b/drivers/gpu/drm/omap/omap_fbdev.c
new file mode 100644
index 00000000000..84b94dbfea4
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_fbdev.c
@@ -0,0 +1,298 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_fbdev.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_gpu.h>
+#include "omap_gpu_priv.h"
+
+#include "drm_crtc.h"
+#include "drm_fb_helper.h"
+
+/*
+ * fbdev funcs, to implement legacy fbdev interface on top of drm driver
+ */
+
+#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
+
+struct omap_fbdev {
+ struct drm_fb_helper base;
+ struct drm_framebuffer *fb;
+};
+
+static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t res;
+
+ res = fb_sys_write(fbi, buf, count, ppos);
+ omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
+
+ return res;
+}
+
+static void omap_fbdev_fillrect(struct fb_info *fbi,
+ const struct fb_fillrect *rect)
+{
+ sys_fillrect(fbi, rect);
+ omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
+}
+
+static void omap_fbdev_copyarea(struct fb_info *fbi,
+ const struct fb_copyarea *area)
+{
+ sys_copyarea(fbi, area);
+ omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
+}
+
+static void omap_fbdev_imageblit(struct fb_info *fbi,
+ const struct fb_image *image)
+{
+ sys_imageblit(fbi, image);
+ omap_fbdev_flush(fbi, image->dx, image->dy,
+ image->width, image->height);
+}
+
+static struct fb_ops omap_fb_ops = {
+ .owner = THIS_MODULE,
+
+ /* Note: to properly handle manual update displays, we wrap the
+ * basic fbdev ops which write to the framebuffer
+ */
+ .fb_read = fb_sys_read,
+ .fb_write = omap_fbdev_write,
+ .fb_fillrect = omap_fbdev_fillrect,
+ .fb_copyarea = omap_fbdev_copyarea,
+ .fb_imageblit = omap_fbdev_imageblit,
+
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int omap_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct omap_fbdev *fbdev = to_omap_fbdev(helper);
+ struct drm_device *dev = helper->dev;
+ struct fb_info *fbi;
+ struct drm_mode_fb_cmd mode_cmd = {0};
+ struct device *device = &dev->platformdev->dev;
+ int ret;
+
+ /* only doing ARGB32 since this is what is needed to alpha-blend
+ * with video overlays:
+ */
+ sizes->surface_bpp = 32;
+ sizes->surface_depth = 32;
+
+ DBG("create fbdev: %dx%d@%d", sizes->surface_width,
+ sizes->surface_height, sizes->surface_bpp);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+
+ mode_cmd.bpp = sizes->surface_bpp;
+ mode_cmd.depth = sizes->surface_depth;
+
+ mutex_lock(&dev->struct_mutex);
+
+ fbi = framebuffer_alloc(0, device);
+ if (!fbi) {
+ dev_err(dev->dev, "failed to allocate fb info\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ DBG("fbi=%p, dev=%p", fbi, dev);
+
+ fbdev->fb = omap_framebuffer_init(dev, &mode_cmd);
+ if (!fbdev->fb) {
+ dev_err(dev->dev, "failed to allocate fb\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ helper->fb = fbdev->fb;
+ helper->fbdev = fbi;
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_DEFAULT;
+ fbi->fbops = &omap_fb_ops;
+
+ strcpy(fbi->fix.id, MODULE_NAME);
+
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ omap_fbdev_update(helper, fbdev->fb);
+
+ DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
+ DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+
+fail:
+ mutex_unlock(&dev->struct_mutex);
+ // TODO cleanup?
+ return ret;
+}
+
+static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
+ u16 red, u16 green, u16 blue, int regno)
+{
+ DBG("fbdev: set gamma");
+ // XXX ignore?
+}
+
+static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
+ u16 *red, u16 *green, u16 *blue, int regno)
+{
+ DBG("fbdev: get gamma");
+ // XXX ignore?
+}
+
+static int omap_fbdev_probe(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ int new_fb = 0;
+ int ret;
+
+ if (!helper->fb) {
+ ret = omap_fbdev_create(helper, sizes);
+ if (ret)
+ return ret;
+ new_fb = 1;
+ }
+ return new_fb;
+}
+
+static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+ .gamma_set = omap_crtc_fb_gamma_set,
+ .gamma_get = omap_crtc_fb_gamma_get,
+ .fb_probe = omap_fbdev_probe,
+};
+
+static struct drm_fb_helper * get_fb(struct fb_info *fbi)
+{
+ if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
+ /* these are not the fb's you're looking for */
+ return NULL;
+ }
+ return fbi->par;
+}
+
+void omap_fbdev_update(struct drm_fb_helper *helper,
+ struct drm_framebuffer *fb)
+{
+ struct fb_info *fbi = helper->fbdev;
+ struct drm_device *dev = helper->dev;
+ struct omap_fbdev *fbdev = to_omap_fbdev(helper);
+ unsigned long paddr;
+ void __iomem *vaddr;
+ int size, screen_width;
+
+ DBG("update fbdev: %dx%d, fbi=%p", fb->width, fb->height, fbi);
+
+ fbdev->fb = fb;
+
+ drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
+ drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+ size = omap_framebuffer_get_buffer(fbdev->fb, 0, 0,
+ &vaddr, &paddr, &screen_width);
+
+ dev->mode_config.fb_base = paddr;
+
+ fbi->screen_base = vaddr;
+ fbi->screen_size = size;
+ fbi->fix.smem_start = paddr;
+ fbi->fix.smem_len = size;
+}
+
+struct drm_connector * omap_fbdev_get_next_connector(struct fb_info *fbi,
+ struct drm_connector *from)
+{
+ struct drm_fb_helper *helper = get_fb(fbi);
+
+ if (!helper)
+ return NULL;
+
+ return omap_framebuffer_get_next_connector(helper->fb, from);
+}
+EXPORT_SYMBOL(omap_fbdev_get_next_connector);
+
+/* flush an area of the framebuffer (in case of manual update display that
+ * is not automatically flushed)
+ */
+void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
+{
+ struct drm_fb_helper *helper = get_fb(fbi);
+
+ if (!helper)
+ return;
+
+ VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
+
+ omap_framebuffer_flush(helper->fb, x, y, w, h);
+}
+EXPORT_SYMBOL(omap_fbdev_flush);
+
+/* initialize fbdev helper */
+struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev)
+{
+ struct omap_gpu_private *priv = dev->dev_private;
+ struct omap_fbdev *fbdev = NULL;
+ struct drm_fb_helper *helper;
+ int ret = 0;
+
+ fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev) {
+ dev_err(dev->dev, "could not allocate fbdev\n");
+ goto fail;
+ }
+
+ helper = &fbdev->base;
+
+ helper->funcs = &omap_fb_helper_funcs;
+
+ ret = drm_fb_helper_init(dev, helper, priv->num_crtcs, 4);
+ if (ret) {
+ dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
+ goto fail;
+ }
+
+ drm_fb_helper_single_add_all_connectors(helper);
+ drm_fb_helper_initial_config(helper, 32);
+
+ priv->fbdev = helper;
+
+ return helper;
+
+fail:
+ if (fbdev) {
+ kfree(fbdev);
+ }
+ return NULL;
+}
diff --git a/drivers/gpu/drm/omap/omap_gpu.c b/drivers/gpu/drm/omap/omap_gpu.c
new file mode 100644
index 00000000000..15111700853
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_gpu.c
@@ -0,0 +1,751 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_gpu.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_gpu.h>
+#include "omap_gpu_priv.h"
+
+#include "drm_crtc_helper.h"
+#include "drm_fb_helper.h"
+
+#define DRIVER_NAME MODULE_NAME
+#define DRIVER_DESC "OMAP GPU"
+#define DRIVER_DATE "20110403"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+struct drm_device *drm_device;
+
+/* TODO: think about how to handle more than one plugin.. ie. some ops
+ * me might want to stop on the first plugin that doesn't return an
+ * error, etc..
+ */
+LIST_HEAD(plugin_list);
+
+/* keep track of whether we are already loaded.. we may need to call
+ * plugin's load() if they register after we are already loaded
+ */
+static bool loaded = false;
+
+/*
+ * mode config funcs
+ */
+
+/* Notes about mapping DSS and DRM entities:
+ * CRTC: overlay
+ * encoder: manager.. with some extension to allow one primary CRTC
+ * and zero or more video CRTC's to be mapped to one encoder?
+ * connector: dssdev.. manager can be attached/detached from different
+ * devices
+ */
+
+static void omap_fb_output_poll_changed(struct drm_device *dev)
+{
+ struct omap_gpu_private *priv = dev->dev_private;
+ DBG("dev=%p", dev);
+ if (priv->fbdev) {
+ drm_fb_helper_hotplug_event(priv->fbdev);
+ }
+}
+
+static struct drm_mode_config_funcs omap_mode_config_funcs = {
+ .fb_create = omap_framebuffer_create,
+ .output_poll_changed = omap_fb_output_poll_changed,
+};
+
+static int get_connector_type(struct omap_dss_device *dssdev)
+{
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+ if (!strcmp(dssdev->name, "dvi"))
+ return DRM_MODE_CONNECTOR_DVID;
+ default:
+ return DRM_MODE_CONNECTOR_Unknown;
+ }
+}
+
+static int omap_gpu_notifier(struct notifier_block *nb,
+ unsigned long evt, void *arg)
+{
+ switch (evt) {
+ case OMAP_DSS_SIZE_CHANGE:
+ case OMAP_DSS_HOTPLUG_CONNECT:
+ case OMAP_DSS_HOTPLUG_DISCONNECT: {
+ struct drm_device *dev = drm_device;
+ DBG("hotplug event: evt=%d, dev=%p", evt, dev);
+ if (dev) {
+ drm_sysfs_hotplug_event(dev);
+ }
+ return NOTIFY_OK;
+ }
+ default: /* don't care about other events for now */
+ return NOTIFY_DONE;
+ }
+}
+
+static void dump_video_chains(void)
+{
+ int i;
+
+ DBG("dumping video chains: ");
+ for (i = 0; i < omap_dss_get_num_overlays(); i++) {
+ struct omap_overlay *ovl = omap_dss_get_overlay(i);
+ struct omap_overlay_manager *mgr = ovl->manager;
+ struct omap_dss_device *dssdev = mgr ? mgr->device : NULL;
+ if (dssdev) {
+ DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
+ dssdev->name);
+ } else if (mgr) {
+ DBG("%d: %s -> %s", i, ovl->name, mgr->name);
+ } else {
+ DBG("%d: %s", i, ovl->name);
+ }
+ }
+}
+
+static int omap_modeset_init(struct drm_device *dev)
+{
+ const struct omap_gpu_platform_data *pdata = dev->dev->platform_data;
+ struct omap_gpu_private *priv = dev->dev_private;
+ struct omap_dss_device *dssdev = NULL;
+ int i, j;
+ unsigned int connected_connectors = 0;
+
+ /* create encoders for each manager */
+ int create_encoder(int i) {
+ struct omap_overlay_manager *mgr =
+ omap_dss_get_overlay_manager(i);
+ struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
+
+ if (!encoder) {
+ dev_err(dev->dev, "could not create encoder\n");
+ return -ENOMEM;
+ }
+
+ priv->encoders[priv->num_encoders++] = encoder;
+
+ return 0;
+ }
+
+ /* create connectors for each display device */
+ int create_connector(struct omap_dss_device *dssdev) {
+ static struct notifier_block *notifier;
+ struct drm_connector *connector;
+
+ if (!dssdev->driver) {
+ dev_warn(dev->dev, "%s has no driver.. skipping it\n",
+ dssdev->name);
+ return 0;
+ }
+
+ if (!(dssdev->driver->get_timings ||
+ dssdev->driver->get_edid)) {
+ dev_warn(dev->dev, "%s driver does not support "
+ "get_timings or get_edid.. skipping it!\n",
+ dssdev->name);
+ return 0;
+ }
+
+ connector = omap_connector_init(dev,
+ get_connector_type(dssdev), dssdev);
+
+ if (!connector) {
+ dev_err(dev->dev, "could not create connector\n");
+ return -ENOMEM;
+ }
+
+ /* track what is already connected.. rather than looping thru
+ * all connectors twice later, first for connected then for
+ * remainder (which could be a race condition if connected
+ * status changes)
+ */
+ if (omap_connector_detect(connector, true) ==
+ connector_status_connected) {
+ connected_connectors |= (1 << priv->num_connectors);
+ }
+
+ priv->connectors[priv->num_connectors++] = connector;
+
+ notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
+ notifier->notifier_call = omap_gpu_notifier;
+ omap_dss_add_notify(dssdev, notifier);
+
+ for (j = 0; j < priv->num_encoders; j++) {
+ struct omap_overlay_manager *mgr =
+ omap_encoder_get_manager(priv->encoders[j]);
+ if (mgr->device == dssdev) {
+ drm_mode_connector_attach_encoder(connector,
+ priv->encoders[j]);
+ }
+ }
+
+ return 0;
+ }
+
+ /* create up to max_overlays CRTCs mapping to overlays.. by default,
+ * connect the overlays to different managers/encoders, giving priority
+ * to encoders connected to connectors with a detected connection
+ */
+ int create_crtc(int i) {
+ struct omap_overlay *ovl = omap_dss_get_overlay(i);
+ struct omap_overlay_manager *mgr = NULL;
+ struct drm_crtc *crtc;
+
+ if (ovl->manager) {
+ DBG("disconnecting %s from %s", ovl->name,
+ ovl->manager->name);
+ ovl->unset_manager(ovl);
+ }
+
+ /* find next best connector, ones with detected connection first
+ */
+ while (j < priv->num_connectors && !mgr) {
+ if (connected_connectors & (1 << j)) {
+ struct drm_encoder * encoder =
+ omap_connector_attached_encoder(
+ priv->connectors[j]);
+ if (encoder) {
+ mgr = omap_encoder_get_manager(encoder);
+ }
+ }
+ j++;
+ }
+
+ /* if we couldn't find another connected connector, lets start
+ * looking at the unconnected connectors:
+ */
+ while (j < 2 * priv->num_connectors && !mgr) {
+ int idx = j - priv->num_connectors;
+ if (!(connected_connectors & (1 << idx))) {
+ struct drm_encoder * encoder =
+ omap_connector_attached_encoder(
+ priv->connectors[idx]);
+ if (encoder) {
+ mgr = omap_encoder_get_manager(encoder);
+ }
+ }
+ j++;
+ }
+
+ if (mgr) {
+ DBG("connecting %s to %s", ovl->name, mgr->name);
+ ovl->set_manager(ovl, mgr);
+ }
+
+ crtc = omap_crtc_init(dev, ovl);
+
+ if (!crtc) {
+ dev_err(dev->dev, "could not create CRTC\n");
+ return -ENOMEM;
+ }
+
+ priv->crtcs[priv->num_crtcs++] = crtc;
+
+ return 0;
+ }
+
+ drm_mode_config_init(dev);
+
+ if (pdata) {
+ /* if platform data is provided by the board file, use it to
+ * control which overlays, managers, and devices we own.
+ */
+ for (i = 0; i < pdata->mgr_cnt; i++) {
+ if (create_encoder(pdata->mgr_ids[i])) {
+ goto fail;
+ }
+ }
+
+ for (i = 0; i < pdata->dev_cnt; i++) {
+ int m(struct omap_dss_device *dssdev, void *data) {
+ return ! strcmp(dssdev->name, data);
+ }
+ struct omap_dss_device *dssdev =
+ omap_dss_find_device(
+ (void *)pdata->dev_names[i], m);
+ if (!dssdev) {
+ dev_warn(dev->dev, "no such dssdev: %s\n",
+ pdata->dev_names[i]);
+ continue;
+ }
+ if (create_connector(dssdev)) {
+ goto fail;
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < pdata->ovl_cnt; i++) {
+ if (create_crtc(pdata->ovl_ids[i])) {
+ goto fail;
+ }
+ }
+ } else {
+ /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
+ * to make educated guesses about everything else
+ */
+ int max_overlays = min(omap_dss_get_num_overlays(),
+ CONFIG_DRM_OMAP_NUM_CRTCS);
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
+ if (create_encoder(i)) {
+ goto fail;
+ }
+ }
+
+ for_each_dss_dev(dssdev) {
+ if (create_connector(dssdev)) {
+ goto fail;
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < max_overlays; i++) {
+ if (create_crtc(i)) {
+ goto fail;
+ }
+ }
+ }
+
+ /* for now keep the mapping of CRTCs and encoders static.. */
+ for (i = 0; i < priv->num_encoders; i++) {
+ struct drm_encoder *encoder = priv->encoders[i];
+ struct omap_overlay_manager *mgr =
+ omap_encoder_get_manager(encoder);
+
+ encoder->possible_crtcs = 0;
+
+ for (j = 0; j < priv->num_crtcs; j++) {
+ struct omap_overlay *ovl =
+ omap_crtc_get_overlay(priv->crtcs[j]);
+ if (ovl->manager == mgr) {
+ encoder->possible_crtcs |= (1 << j);
+ }
+ }
+
+ DBG("%s: possible_crtcs=%08x", mgr->name,
+ encoder->possible_crtcs);
+ }
+
+ dump_video_chains();
+
+ dev->mode_config.min_width = 640;
+ dev->mode_config.min_height = 480;
+
+ /* note: pvr can't currently handle dst surfaces larger than 2k by 2k */
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+
+ dev->mode_config.funcs = &omap_mode_config_funcs;
+
+ drm_kms_helper_poll_init(dev);
+
+ return 0;
+
+fail:
+ /* TODO: cleanup what has been created so far */
+ return -EINVAL;
+}
+
+/*
+ * drm driver funcs
+ */
+
+/**
+ * load - setup chip and create an initial config
+ * @dev: DRM device
+ * @flags: startup flags
+ *
+ * The driver load routine has to do several things:
+ * - initialize the memory manager
+ * - allocate initial config memory
+ * - setup the DRM framebuffer with the allocated memory
+ */
+static int dev_load(struct drm_device *dev, unsigned long flags)
+{
+ struct omap_gpu_private *priv;
+ struct omap_gpu_plugin *plugin;
+ int ret;
+
+ DBG("load: dev=%p", dev);
+
+ drm_device = dev;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev->dev, "could not allocate priv\n");
+ return -1;
+ }
+
+ dev->dev_private = priv;
+
+ ret = omap_modeset_init(dev);
+ if (ret) {
+ dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
+ // hmm
+ //return ret;
+ }
+
+ priv->fbdev = omap_fbdev_init(dev);
+ if (!priv->fbdev) {
+ dev_err(dev->dev, "omap_fbdev_init failed\n");
+ ret = -ENOMEM;
+ // hmm
+ //return ret;
+ }
+
+ loaded = true;
+
+ list_for_each_entry(plugin, &plugin_list, list) {
+ ret = plugin->load(dev, flags);
+ }
+
+ return 0;
+}
+
+static int dev_unload(struct drm_device *dev)
+{
+ struct omap_gpu_plugin *plugin;
+ int ret;
+
+ DBG("unload: dev=%p", dev);
+
+ list_for_each_entry(plugin, &plugin_list, list) {
+ ret = plugin->unload(dev);
+ }
+
+ drm_kms_helper_poll_fini(dev);
+
+ loaded = false;
+
+ return 0;
+}
+
+static int dev_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct omap_gpu_plugin *plugin;
+ bool found_pvr = false;
+ int ret;
+
+ file->driver_priv = NULL;
+
+ DBG("open: dev=%p, file=%p", dev, file);
+
+ list_for_each_entry(plugin, &plugin_list, list) {
+ if (!strcmp(DRIVER_NAME "_pvr", plugin->name)) {
+ found_pvr = true;
+ break;
+ }
+ }
+
+ if (!found_pvr) {
+ DBG("open: PVR submodule not loaded.. let's try now");
+ request_module(DRIVER_NAME "_pvr");
+ }
+
+ list_for_each_entry(plugin, &plugin_list, list) {
+ ret = plugin->open(dev, file);
+ }
+
+ return 0;
+}
+
+static int dev_firstopen(struct drm_device *dev)
+{
+ DBG("firstopen: dev=%p", dev);
+ return 0;
+}
+
+/**
+ * lastclose - clean up after all DRM clients have exited
+ * @dev: DRM device
+ *
+ * Take care of cleaning up after all DRM clients have exited. In the
+ * mode setting case, we want to restore the kernel's initial mode (just
+ * in case the last client left us in a bad state).
+ *
+ * Additionally, in the non-mode setting case, we'll tear down the AGP
+ * and DMA structures, since the kernel won't be using them, and clean
+ * up any GEM state.
+ */
+static void dev_lastclose(struct drm_device * dev)
+{
+ DBG("lastclose: dev=%p", dev);
+}
+
+static void dev_preclose(struct drm_device * dev, struct drm_file *file)
+{
+ DBG("preclose: dev=%p", dev);
+}
+
+static void dev_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ struct omap_gpu_plugin *plugin;
+ int ret;
+
+ DBG("postclose: dev=%p, file=%p", dev, file);
+
+ list_for_each_entry(plugin, &plugin_list, list) {
+ ret = plugin->release(dev, file);
+ }
+
+ return;
+}
+
+/**
+ * enable_vblank - enable vblank interrupt events
+ * @dev: DRM device
+ * @crtc: which irq to enable
+ *
+ * Enable vblank interrupts for @crtc. If the device doesn't have
+ * a hardware vblank counter, this routine should be a no-op, since
+ * interrupts will have to stay on to keep the count accurate.
+ *
+ * RETURNS
+ * Zero on success, appropriate errno if the given @crtc's vblank
+ * interrupt cannot be enabled.
+ */
+static int dev_enable_vblank(struct drm_device *dev, int crtc)
+{
+ DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
+ return 0;
+}
+
+/**
+ * disable_vblank - disable vblank interrupt events
+ * @dev: DRM device
+ * @crtc: which irq to enable
+ *
+ * Disable vblank interrupts for @crtc. If the device doesn't have
+ * a hardware vblank counter, this routine should be a no-op, since
+ * interrupts will have to stay on to keep the count accurate.
+ */
+static void dev_disable_vblank(struct drm_device *dev, int crtc)
+{
+ DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
+}
+
+/**
+ * Called by \c drm_device_is_agp. Typically used to determine if a
+ * card is really attached to AGP or not.
+ *
+ * \param dev DRM device handle
+ *
+ * \returns
+ * One of three values is returned depending on whether or not the
+ * card is absolutely \b not AGP (return of 0), absolutely \b is AGP
+ * (return of 1), or may or may not be AGP (return of 2).
+ */
+static int dev_device_is_agp(struct drm_device *dev)
+{
+ return 0;
+}
+
+static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
+{
+ return IRQ_HANDLED;
+}
+
+static void dev_irq_preinstall(struct drm_device *dev)
+{
+ DBG("irq_preinstall: dev=%p", dev);
+}
+
+static int dev_irq_postinstall(struct drm_device *dev)
+{
+ DBG("irq_postinstall: dev=%p", dev);
+ return 0;
+}
+
+static void dev_irq_uninstall(struct drm_device *dev)
+{
+ DBG("irq_uninstall: dev=%p", dev);
+}
+
+static int fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap_gpu_plugin *plugin;
+ int ret = 0;
+
+ list_for_each_entry(plugin, &plugin_list, list) {
+ ret = plugin->mmap(file, vma);
+ if (!ret) {
+ /* on first plugin that succeeds, bail out of iteration */
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static struct drm_driver omap_gpu_driver;
+
+static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
+{
+ DBG("pdev_suspend");
+ return 0;
+}
+
+static int pdev_resume(struct platform_device *device)
+{
+ DBG("pdev_resume");
+ return 0;
+}
+
+static void pdev_shutdown(struct platform_device *device)
+{
+ DBG("pdev_shutdown");
+}
+
+static int pdev_probe(struct platform_device *device)
+{
+ DBG("pdev_probe: %s", device->name);
+ return drm_get_platform_dev(device, &omap_gpu_driver);
+}
+
+static int pdev_remove(struct platform_device *device)
+{
+ DBG("pdev_remove");
+ drm_put_dev(drm_device);
+ return 0;
+}
+
+struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {{0}};
+
+static struct drm_driver omap_gpu_driver = {
+ .driver_features = DRIVER_HAVE_IRQ |
+ DRIVER_USE_PLATFORM_DEVICE | DRIVER_MODESET,
+ .load = dev_load,
+ .unload = dev_unload,
+ .open = dev_open,
+ .firstopen = dev_firstopen,
+ .lastclose = dev_lastclose,
+ .preclose = dev_preclose,
+ .postclose = dev_postclose,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = dev_enable_vblank,
+ .disable_vblank = dev_disable_vblank,
+ .device_is_agp = dev_device_is_agp,
+ .irq_preinstall = dev_irq_preinstall,
+ .irq_postinstall = dev_irq_postinstall,
+ .irq_uninstall = dev_irq_uninstall,
+ .irq_handler = dev_irq_handler,
+ .reclaim_buffers = drm_core_reclaim_buffers,
+ .ioctls = ioctls,
+ .num_ioctls = 0,
+ .fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ .mmap = fop_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+ .read = drm_read,
+ },
+ .platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = pdev_probe,
+ .remove = pdev_remove,
+ .suspend = pdev_suspend,
+ .resume = pdev_resume,
+ .shutdown = pdev_shutdown,
+ },
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+int omap_gpu_register_plugin(struct omap_gpu_plugin *plugin)
+{
+ struct drm_device *dev = drm_device;
+ int i;
+
+ DBG("register plugin: %p (%s)", plugin, plugin->name);
+
+ /* XXX: PVR code uses drm_file->driver_priv...
+ * need to come up with some sane way to handle this.
+ */
+
+ list_add_tail(&plugin->list, &plugin_list);
+
+ /* register the plugin's ioctl's */
+ for (i = 0; i < plugin->num_ioctls; i++) {
+ int nr = i + plugin->ioctl_start;
+
+ /* check for out of bounds ioctl nr or already registered ioctl */
+ if (nr > ARRAY_SIZE(ioctls) || ioctls[nr].func) {
+ dev_err(dev->dev, "invalid ioctl: %d (nr=%d)\n", i, nr);
+ return -EINVAL;
+ }
+
+ DBG("register ioctl: %d %08x", nr, plugin->ioctls[i].cmd);
+
+ ioctls[nr] = plugin->ioctls[i];
+
+ if (nr >= omap_gpu_driver.num_ioctls) {
+ omap_gpu_driver.num_ioctls = nr + 1;
+ }
+ }
+
+ if (loaded) {
+ plugin->load(dev, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_gpu_register_plugin);
+
+int omap_gpu_unregister_plugin(struct omap_gpu_plugin *plugin)
+{
+ list_del(&plugin->list);
+ return 0;
+}
+EXPORT_SYMBOL(omap_gpu_unregister_plugin);
+
+struct fb_info * omap_gpu_get_fbdev(struct drm_device *dev)
+{
+ struct omap_gpu_private *priv = dev->dev_private;
+ return priv->fbdev->fbdev;
+}
+EXPORT_SYMBOL(omap_gpu_get_fbdev);
+
+static int __init omap_gpu_init(void)
+{
+ DBG("init");
+ return drm_init(&omap_gpu_driver);
+}
+
+static void __exit omap_gpu_fini(void)
+{
+ DBG("fini");
+ drm_exit(&omap_gpu_driver);
+}
+
+/* need late_initcall() so we load after dss_driver's are loaded */
+late_initcall(omap_gpu_init);
+module_exit(omap_gpu_fini);
+
+MODULE_AUTHOR("Rob Clark <rob@ti.com>");
+MODULE_DESCRIPTION("OMAP DRM Display Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/omap/omap_gpu_priv.h b/drivers/gpu/drm/omap/omap_gpu_priv.h
new file mode 100644
index 00000000000..4af361d83b5
--- /dev/null
+++ b/drivers/gpu/drm/omap/omap_gpu_priv.h
@@ -0,0 +1,80 @@
+/*
+ * linux/drivers/gpu/drm/omap/omap_gpu_priv.h
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP_GPU_PRIV_H__
+#define __OMAP_GPU_PRIV_H__
+
+#include <plat/display.h>
+#include <linux/module.h>
+
+#define DBG(fmt,...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+#define VERB(fmt,...) do { } while (0) /* verbose debug */
+
+#define MODULE_NAME "omap_gpu"
+
+struct omap_gpu_private {
+ int num_crtcs;
+ struct drm_crtc *crtcs[8];
+ int num_encoders;
+ struct drm_encoder *encoders[8];
+ int num_connectors;
+ struct drm_connector *connectors[8];
+
+ struct drm_fb_helper *fbdev;
+
+ /* for now, we statically create a single framebuffer per device, since
+ * there is not yet any good way to dynamically allocate/free contiguous
+ * memory..
+ */
+ struct drm_framebuffer *fb;
+};
+
+struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev);
+void omap_fbdev_update(struct drm_fb_helper *helper,
+ struct drm_framebuffer *fb);
+
+struct drm_crtc * omap_crtc_init(struct drm_device *dev,
+ struct omap_overlay *ovl);
+struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc);
+
+struct drm_encoder * omap_encoder_init(struct drm_device *dev,
+ struct omap_overlay_manager *mgr);
+struct omap_overlay_manager * omap_encoder_get_manager(
+ struct drm_encoder *encoder);
+struct drm_encoder * omap_connector_attached_encoder (
+ struct drm_connector *connector);
+enum drm_connector_status omap_connector_detect(
+ struct drm_connector *connector, bool force);
+
+struct drm_connector * omap_connector_init(struct drm_device *dev,
+ int connector_type, struct omap_dss_device *dssdev);
+void omap_connector_mode_set(struct drm_connector *connector,
+ struct drm_display_mode *mode);
+void omap_connector_flush(struct drm_connector *connector,
+ int x, int y, int w, int h);
+void omap_connector_dpms(struct drm_connector *connector, int mode);
+
+struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd);
+struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd);
+int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
+ void **vaddr, unsigned long *paddr, int *screen_width);
+
+#endif /* __OMAP_GPU_PRIV_H__ */