summaryrefslogtreecommitdiffstats
path: root/drivers/staging/omapdrm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/omapdrm')
-rw-r--r--drivers/staging/omapdrm/Makefile1
-rw-r--r--drivers/staging/omapdrm/omap_crtc.c191
-rw-r--r--drivers/staging/omapdrm/omap_drv.c81
-rw-r--r--drivers/staging/omapdrm/omap_drv.h52
-rw-r--r--drivers/staging/omapdrm/omap_fb.c224
-rw-r--r--drivers/staging/omapdrm/omap_fbdev.c59
-rw-r--r--drivers/staging/omapdrm/omap_gem.c23
-rw-r--r--drivers/staging/omapdrm/omap_plane.c344
-rw-r--r--drivers/staging/omapdrm/omap_priv.h12
9 files changed, 717 insertions, 270 deletions
diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile
index 592cf69020cd..d9cdc120d122 100644
--- a/drivers/staging/omapdrm/Makefile
+++ b/drivers/staging/omapdrm/Makefile
@@ -7,6 +7,7 @@ ccflags-y := -Iinclude/drm -Werror
omapdrm-y := omap_drv.o \
omap_debugfs.o \
omap_crtc.o \
+ omap_plane.o \
omap_encoder.o \
omap_connector.o \
omap_fb.o \
diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
index cffdf5e12394..17ca163e5896 100644
--- a/drivers/staging/omapdrm/omap_crtc.c
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -27,196 +27,95 @@
struct omap_crtc {
struct drm_crtc base;
- struct omap_overlay *ovl;
- struct omap_overlay_info info;
+ struct drm_plane *plane;
+ const char *name;
int id;
- /* if there is a pending flip, this will be non-null: */
+ /* if there is a pending flip, these will be non-null: */
struct drm_pending_vblank_event *event;
+ struct drm_framebuffer *old_fb;
};
-/* push changes down to dss2 */
-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 %08x", info->pos_x, info->pos_y, 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 settings\n");
- return ret;
- }
- }
-
- if (info->enabled) {
- omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y,
- crtc->fb->width, crtc->fb->height);
- }
-
- return 0;
-}
-
-/* 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 update_scanout(struct drm_crtc *crtc)
-{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- dma_addr_t paddr;
- unsigned int screen_width;
-
- omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y,
- NULL, &paddr, &screen_width);
-
- DBG("%s: %d,%d: %08x (%d)", omap_crtc->ovl->name,
- crtc->x, crtc->y, (u32)paddr, screen_width);
-
- omap_crtc->info.paddr = paddr;
- 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);
+ /* not supported.. at least not yet */
}
static void omap_crtc_destroy(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- DBG("%s", omap_crtc->ovl->name);
+ omap_crtc->plane->funcs->destroy(omap_crtc->plane);
drm_crtc_cleanup(crtc);
kfree(omap_crtc);
}
static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
{
+ struct omap_drm_private *priv = crtc->dev->dev_private;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ int i;
- DBG("%s: %d", omap_crtc->ovl->name, mode);
+ WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
- if (mode == DRM_MODE_DPMS_ON) {
- update_scanout(crtc);
- omap_crtc->info.enabled = true;
- } else {
- omap_crtc->info.enabled = false;
+ for (i = 0; i < priv->num_planes; i++) {
+ struct drm_plane *plane = priv->planes[i];
+ if (plane->crtc == crtc)
+ WARN_ON(omap_plane_dpms(plane, mode));
}
-
- WARN_ON(commit(crtc));
}
static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- 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 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);
+ struct drm_plane *plane = omap_crtc->plane;
- 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_RGB24U;
- omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA;
- omap_crtc->info.rotation = OMAP_DSS_ROT_0;
- 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 /* re-enable when these are available in DSS2 driver */
- omap_crtc->info.zorder = 3; /* GUI in the front, video behind */
- 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
-
- update_scanout(crtc);
-
- return 0;
+ return omap_plane_mode_set(plane, crtc, crtc->fb,
+ 0, 0, mode->hdisplay, mode->vdisplay,
+ x << 16, y << 16,
+ mode->hdisplay << 16, mode->vdisplay << 16);
}
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);
-
+ DBG("%s", omap_crtc->name);
omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
static void omap_crtc_commit(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- DBG("%s", omap_crtc->ovl->name);
+ DBG("%s", omap_crtc->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 drm_framebuffer *old_fb)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_plane *plane = omap_crtc->plane;
+ struct drm_display_mode *mode = &crtc->mode;
- DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb);
-
- update_scanout(crtc);
-
- return commit(crtc);
+ return plane->funcs->update_plane(plane, crtc, crtc->fb,
+ 0, 0, mode->hdisplay, mode->vdisplay,
+ x << 16, y << 16,
+ mode->hdisplay << 16, mode->vdisplay << 16);
}
static 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 void page_flip_cb(void *arg)
@@ -225,15 +124,16 @@ static void page_flip_cb(void *arg)
struct drm_device *dev = crtc->dev;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct drm_pending_vblank_event *event = omap_crtc->event;
+ struct drm_framebuffer *old_fb = omap_crtc->old_fb;
struct timeval now;
unsigned long flags;
WARN_ON(!event);
omap_crtc->event = NULL;
+ omap_crtc->old_fb = NULL;
- update_scanout(crtc);
- WARN_ON(commit(crtc));
+ omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
/* wakeup userspace */
/* TODO: this should happen *after* flip in vsync IRQ handler */
@@ -264,10 +164,11 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
return -EINVAL;
}
- crtc->fb = fb;
+ omap_crtc->old_fb = crtc->fb;
omap_crtc->event = event;
+ crtc->fb = fb;
- omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ,
+ omap_gem_op_async(omap_framebuffer_bo(fb, 0), OMAP_GEM_READ,
page_flip_cb, crtc);
return 0;
@@ -290,12 +191,6 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
.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, int id)
@@ -310,9 +205,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
goto fail;
}
- omap_crtc->ovl = ovl;
- omap_crtc->id = id;
crtc = &omap_crtc->base;
+
+ omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true);
+ omap_crtc->plane->crtc = crtc;
+ omap_crtc->name = ovl->name;
+ omap_crtc->id = id;
+
drm_crtc_init(dev, crtc, &omap_crtc_funcs);
drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
index 602aa2dd49c8..3bbea9aac404 100644
--- a/drivers/staging/omapdrm/omap_drv.c
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -204,12 +204,6 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl,
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) {
@@ -245,11 +239,6 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl,
(*j)++;
}
- if (mgr) {
- DBG("connecting %s to %s", ovl->name, mgr->name);
- ovl->set_manager(ovl, mgr);
- }
-
crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
if (!crtc) {
@@ -265,6 +254,26 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl,
return 0;
}
+static int create_plane(struct drm_device *dev, struct omap_overlay *ovl,
+ unsigned int possible_crtcs)
+{
+ struct omap_drm_private *priv = dev->dev_private;
+ struct drm_plane *plane =
+ omap_plane_init(dev, ovl, possible_crtcs, false);
+
+ if (!plane) {
+ dev_err(dev->dev, "could not create plane: %s\n",
+ ovl->name);
+ return -ENOMEM;
+ }
+
+ BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
+
+ priv->planes[priv->num_planes++] = plane;
+
+ return 0;
+}
+
static int match_dev_name(struct omap_dss_device *dssdev, void *data)
{
return !strcmp(dssdev->name, data);
@@ -332,6 +341,12 @@ static int omap_modeset_init(struct drm_device *dev)
omap_dss_get_overlay(kms_pdata->ovl_ids[i]);
create_crtc(dev, ovl, &j, connected_connectors);
}
+
+ for (i = 0; i < kms_pdata->pln_cnt; i++) {
+ struct omap_overlay *ovl =
+ omap_dss_get_overlay(kms_pdata->pln_ids[i]);
+ create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
+ }
} else {
/* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
* to make educated guesses about everything else
@@ -353,6 +368,12 @@ static int omap_modeset_init(struct drm_device *dev)
create_crtc(dev, omap_dss_get_overlay(i),
&j, connected_connectors);
}
+
+ /* use any remaining overlays as drm planes */
+ for (; i < omap_dss_get_num_overlays(); i++) {
+ struct omap_overlay *ovl = omap_dss_get_overlay(i);
+ create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
+ }
}
/* for now keep the mapping of CRTCs and encoders static.. */
@@ -361,15 +382,7 @@ static int omap_modeset_init(struct drm_device *dev)
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);
- }
- }
+ encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
DBG("%s: possible_crtcs=%08x", mgr->name,
encoder->possible_crtcs);
@@ -377,8 +390,8 @@ static int omap_modeset_init(struct drm_device *dev)
dump_video_chains();
- dev->mode_config.min_width = 256;
- dev->mode_config.min_height = 256;
+ dev->mode_config.min_width = 32;
+ dev->mode_config.min_height = 32;
/* note: eventually will need some cpu_is_omapXYZ() type stuff here
* to fill in these limits properly on different OMAP generations..
@@ -708,6 +721,18 @@ static struct vm_operations_struct omap_gem_vm_ops = {
.close = drm_gem_vm_close,
};
+static const struct file_operations omapdriver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ .mmap = omap_gem_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+ .read = drm_read,
+ .llseek = noop_llseek,
+};
+
static struct drm_driver omap_drm_driver = {
.driver_features =
DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM,
@@ -738,17 +763,7 @@ static struct drm_driver omap_drm_driver = {
.dumb_destroy = omap_gem_dumb_destroy,
.ioctls = ioctls,
.num_ioctls = DRM_OMAP_NUM_IOCTLS,
- .fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .unlocked_ioctl = drm_ioctl,
- .release = drm_release,
- .mmap = omap_gem_mmap,
- .poll = drm_poll,
- .fasync = drm_fasync,
- .read = drm_read,
- .llseek = noop_llseek,
- },
+ .fops = &omapdriver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
index 76c42515ecc5..61fe022dda5b 100644
--- a/drivers/staging/omapdrm/omap_drv.h
+++ b/drivers/staging/omapdrm/omap_drv.h
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/types.h>
#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
#include "omap_drm.h"
#include "omap_priv.h"
@@ -41,6 +42,8 @@
struct omap_drm_private {
unsigned int num_crtcs;
struct drm_crtc *crtcs[8];
+ unsigned int num_planes;
+ struct drm_plane *planes[8];
unsigned int num_encoders;
struct drm_encoder *encoders[8];
unsigned int num_connectors;
@@ -61,7 +64,17 @@ void omap_fbdev_free(struct drm_device *dev);
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
struct omap_overlay *ovl, int id);
-struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc);
+
+struct drm_plane *omap_plane_init(struct drm_device *dev,
+ struct omap_overlay *ovl, unsigned int possible_crtcs,
+ bool priv);
+int omap_plane_dpms(struct drm_plane *plane, int mode);
+int omap_plane_mode_set(struct drm_plane *plane,
+ struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h);
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
struct omap_overlay_manager *mgr);
@@ -80,12 +93,14 @@ void omap_connector_flush(struct drm_connector *connector,
int x, int y, int w, int h);
struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
- struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd);
+ struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd);
struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
- struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo);
-struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb);
-int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
- void **vaddr, dma_addr_t *paddr, unsigned int *screen_width);
+ struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
+struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p);
+int omap_framebuffer_pin(struct drm_framebuffer *fb);
+void omap_framebuffer_unpin(struct drm_framebuffer *fb);
+void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y,
+ struct omap_overlay_info *info);
struct drm_connector *omap_framebuffer_get_next_connector(
struct drm_framebuffer *fb, struct drm_connector *from);
void omap_framebuffer_flush(struct drm_framebuffer *fb,
@@ -132,4 +147,29 @@ static inline int align_pitch(int pitch, int width, int bpp)
return ALIGN(pitch, 8 * bytespp);
}
+/* should these be made into common util helpers?
+ */
+
+static inline int objects_lookup(struct drm_device *dev,
+ struct drm_file *filp, uint32_t pixel_format,
+ struct drm_gem_object **bos, uint32_t *handles)
+{
+ int i, n = drm_format_num_planes(pixel_format);
+
+ for (i = 0; i < n; i++) {
+ bos[i] = drm_gem_object_lookup(dev, filp, handles[i]);
+ if (!bos[i]) {
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ while (--i > 0) {
+ drm_gem_object_unreference_unlocked(bos[i]);
+ }
+ return -ENOENT;
+}
+
#endif /* __OMAP_DRV_H__ */
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c
index 0b50c5b3b564..d021a7ec58df 100644
--- a/drivers/staging/omapdrm/omap_fb.c
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -22,18 +22,57 @@
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
-
/*
* framebuffer funcs
*/
+/* per-format info: */
+struct format {
+ enum omap_color_mode dss_format;
+ uint32_t pixel_format;
+ struct {
+ int stride_bpp; /* this times width is stride */
+ int sub_y; /* sub-sample in y dimension */
+ } planes[4];
+ bool yuv;
+};
+
+static const struct format formats[] = {
+ /* 16bpp [A]RGB: */
+ { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */
+ { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */
+ { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */
+ { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */
+ { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */
+ { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */
+ { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */
+ /* 24bpp RGB: */
+ { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */
+ /* 32bpp [A]RGB: */
+ { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */
+ { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */
+ { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */
+ { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */
+ /* YUV: */
+ { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true },
+ { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true },
+ { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true },
+};
+
+/* per-plane info for the fb: */
+struct plane {
+ struct drm_gem_object *bo;
+ uint32_t pitch;
+ uint32_t offset;
+ dma_addr_t paddr;
+};
+
#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
struct omap_framebuffer {
struct drm_framebuffer base;
- struct drm_gem_object *bo;
- int size;
- dma_addr_t paddr;
+ const struct format *format;
+ struct plane planes[4];
};
static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
@@ -41,22 +80,23 @@ static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
unsigned int *handle)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
- return drm_gem_handle_create(file_priv, omap_fb->bo, handle);
+ return drm_gem_handle_create(file_priv,
+ omap_fb->planes[0].bo, handle);
}
static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
{
- struct drm_device *dev = fb->dev;
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ int i, n = drm_format_num_planes(omap_fb->format->pixel_format);
DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
drm_framebuffer_cleanup(fb);
- if (omap_fb->bo) {
- if (omap_fb->paddr && omap_gem_put_paddr(omap_fb->bo))
- dev_err(dev->dev, "could not unmap!\n");
- drm_gem_object_unreference_unlocked(omap_fb->bo);
+ for (i = 0; i < n; i++) {
+ struct plane *plane = &omap_fb->planes[i];
+ if (plane->bo)
+ drm_gem_object_unreference_unlocked(plane->bo);
}
kfree(omap_fb);
@@ -83,37 +123,76 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
.dirty = omap_framebuffer_dirty,
};
-/* returns the buffer size */
-int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
- void **vaddr, dma_addr_t *paddr, unsigned int *screen_width)
+/* pins buffer in preparation for scanout */
+int omap_framebuffer_pin(struct drm_framebuffer *fb)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
- int bpp = fb->bits_per_pixel / 8;
- unsigned long offset;
+ int ret, i, n = drm_format_num_planes(omap_fb->format->pixel_format);
- offset = (x * bpp) + (y * fb->pitch);
+ for (i = 0; i < n; i++) {
+ struct plane *plane = &omap_fb->planes[i];
+ ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true);
+ if (ret)
+ goto fail;
+ }
- if (vaddr) {
- void *bo_vaddr = omap_gem_vaddr(omap_fb->bo);
- /* note: we can only count on having a vaddr for buffers that
- * are allocated physically contiguously to begin with (ie.
- * dma_alloc_coherent()). But this should be ok because it
- * is only used by legacy fbdev
- */
- BUG_ON(IS_ERR_OR_NULL(bo_vaddr));
- *vaddr = bo_vaddr + offset;
+ return 0;
+
+fail:
+ while (--i > 0) {
+ struct plane *plane = &omap_fb->planes[i];
+ omap_gem_put_paddr(plane->bo);
}
+ return ret;
+}
- *paddr = omap_fb->paddr + offset;
- *screen_width = fb->pitch / bpp;
+/* releases buffer when done with scanout */
+void omap_framebuffer_unpin(struct drm_framebuffer *fb)
+{
+ struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ int i, n = drm_format_num_planes(omap_fb->format->pixel_format);
- return omap_fb->size - offset;
+ for (i = 0; i < n; i++) {
+ struct plane *plane = &omap_fb->planes[i];
+ omap_gem_put_paddr(plane->bo);
+ }
}
-struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb)
+/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
+ */
+void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y,
+ struct omap_overlay_info *info)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
- return omap_fb->bo;
+ const struct format *format = omap_fb->format;
+ struct plane *plane = &omap_fb->planes[0];
+ unsigned int offset;
+
+ offset = plane->offset +
+ (x * format->planes[0].stride_bpp) +
+ (y * plane->pitch / format->planes[0].sub_y);
+
+ info->color_mode = format->dss_format;
+ info->paddr = plane->paddr + offset;
+ info->screen_width = plane->pitch / format->planes[0].stride_bpp;
+
+ if (format->dss_format == OMAP_DSS_COLOR_NV12) {
+ plane = &omap_fb->planes[1];
+ offset = plane->offset +
+ (x * format->planes[1].stride_bpp) +
+ (y * plane->pitch / format->planes[1].sub_y);
+ info->p_uv_addr = plane->paddr + offset;
+ } else {
+ info->p_uv_addr = 0;
+ }
+}
+
+struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p)
+{
+ struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ if (p >= drm_format_num_planes(omap_fb->format->pixel_format))
+ return NULL;
+ return omap_fb->planes[p].bo;
}
/* iterate thru all the connectors, returning ones that are attached
@@ -171,39 +250,57 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
}
struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
- struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd)
+ struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd)
{
- struct drm_gem_object *bo;
+ struct drm_gem_object *bos[4];
struct drm_framebuffer *fb;
- bo = drm_gem_object_lookup(dev, file, mode_cmd->handle);
- if (!bo) {
- return ERR_PTR(-ENOENT);
- }
- fb = omap_framebuffer_init(dev, mode_cmd, bo);
- if (!fb) {
- return ERR_PTR(-ENOMEM);
+ int ret;
+
+ ret = objects_lookup(dev, file, mode_cmd->pixel_format,
+ bos, mode_cmd->handles);
+ if (ret)
+ return ERR_PTR(ret);
+
+ fb = omap_framebuffer_init(dev, mode_cmd, bos);
+ if (IS_ERR(fb)) {
+ int i, n = drm_format_num_planes(mode_cmd->pixel_format);
+ for (i = 0; i < n; i++)
+ drm_gem_object_unreference_unlocked(bos[i]);
+ return fb;
}
return fb;
}
struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
- struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo)
+ struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
{
struct omap_framebuffer *omap_fb;
struct drm_framebuffer *fb = NULL;
- int size, ret;
+ const struct format *format = NULL;
+ int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
- DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%d)",
+ DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
dev, mode_cmd, mode_cmd->width, mode_cmd->height,
- mode_cmd->bpp);
+ (char *)&mode_cmd->pixel_format);
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].pixel_format == mode_cmd->pixel_format) {
+ format = &formats[i];
+ break;
+ }
+ }
- /* in case someone tries to feed us a completely bogus stride: */
- mode_cmd->pitch = align_pitch(mode_cmd->pitch,
- mode_cmd->width, mode_cmd->bpp);
+ if (!format) {
+ dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
+ (char *)&mode_cmd->pixel_format);
+ ret = -EINVAL;
+ goto fail;
+ }
omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
if (!omap_fb) {
dev_err(dev->dev, "could not allocate fb\n");
+ ret = -ENOMEM;
goto fail;
}
@@ -216,19 +313,32 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
DBG("create: FB ID: %d (%p)", fb->base.id, fb);
- size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height);
+ omap_fb->format = format;
- if (size > bo->size) {
- dev_err(dev->dev, "provided buffer object is too small!\n");
- goto fail;
- }
+ for (i = 0; i < n; i++) {
+ struct plane *plane = &omap_fb->planes[i];
+ int size, pitch = mode_cmd->pitches[i];
+
+ if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) {
+ dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n",
+ pitch, mode_cmd->width * format->planes[i].stride_bpp);
+ ret = -EINVAL;
+ goto fail;
+ }
- omap_fb->bo = bo;
- omap_fb->size = size;
+ size = pitch * mode_cmd->height / format->planes[i].sub_y;
- if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) {
- dev_err(dev->dev, "could not map (paddr)!\n");
- goto fail;
+ if (size > (bos[i]->size - mode_cmd->offsets[i])) {
+ dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
+ bos[i]->size - mode_cmd->offsets[i], size);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ plane->bo = bos[i];
+ plane->offset = mode_cmd->offsets[i];
+ plane->pitch = mode_cmd->pitches[i];
+ plane->paddr = pitch;
}
drm_helper_mode_fill_fb_struct(fb, mode_cmd);
@@ -239,5 +349,5 @@ fail:
if (fb) {
omap_framebuffer_destroy(fb);
}
- return NULL;
+ return ERR_PTR(ret);
}
diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c
index 093ae2f87b20..96940bbfc6f4 100644
--- a/drivers/staging/omapdrm/omap_fbdev.c
+++ b/drivers/staging/omapdrm/omap_fbdev.c
@@ -129,10 +129,8 @@ static int omap_fbdev_create(struct drm_fb_helper *helper,
struct drm_framebuffer *fb = NULL;
union omap_gem_size gsize;
struct fb_info *fbi = NULL;
- struct drm_mode_fb_cmd mode_cmd = {0};
+ struct drm_mode_fb_cmd2 mode_cmd = {0};
dma_addr_t paddr;
- void __iomem *vaddr;
- int size, screen_width;
int ret;
/* only doing ARGB32 since this is what is needed to alpha-blend
@@ -145,36 +143,56 @@ static int omap_fbdev_create(struct drm_fb_helper *helper,
sizes->surface_height, sizes->surface_bpp,
sizes->fb_width, sizes->fb_height);
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
- mode_cmd.bpp = sizes->surface_bpp;
- mode_cmd.depth = sizes->surface_depth;
-
- mode_cmd.pitch = align_pitch(
- mode_cmd.width * ((mode_cmd.bpp + 7) / 8),
- mode_cmd.width, mode_cmd.bpp);
+ mode_cmd.pitches[0] = align_pitch(
+ mode_cmd.width * ((sizes->surface_bpp + 7) / 8),
+ mode_cmd.width, sizes->surface_bpp);
fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled;
if (fbdev->ywrap_enabled) {
/* need to align pitch to page size if using DMM scrolling */
- mode_cmd.pitch = ALIGN(mode_cmd.pitch, PAGE_SIZE);
+ mode_cmd.pitches[0] = ALIGN(mode_cmd.pitches[0], PAGE_SIZE);
}
/* allocate backing bo */
gsize = (union omap_gem_size){
- .bytes = PAGE_ALIGN(mode_cmd.pitch * mode_cmd.height),
+ .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height),
};
DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index);
fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC);
if (!fbdev->bo) {
dev_err(dev->dev, "failed to allocate buffer object\n");
+ ret = -ENOMEM;
goto fail;
}
- fb = omap_framebuffer_init(dev, &mode_cmd, fbdev->bo);
- if (!fb) {
+ fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo);
+ if (IS_ERR(fb)) {
dev_err(dev->dev, "failed to allocate fb\n");
+ /* note: if fb creation failed, we can't rely on fb destroy
+ * to unref the bo:
+ */
+ drm_gem_object_unreference(fbdev->bo);
+ ret = PTR_ERR(fb);
+ goto fail;
+ }
+
+ /* note: this keeps the bo pinned.. which is perhaps not ideal,
+ * but is needed as long as we use fb_mmap() to mmap to userspace
+ * (since this happens using fix.smem_start). Possibly we could
+ * implement our own mmap using GEM mmap support to avoid this
+ * (non-tiled buffer doesn't need to be pinned for fbcon to write
+ * to it). Then we just need to be sure that we are able to re-
+ * pin it in case of an opps.
+ */
+ ret = omap_gem_get_paddr(fbdev->bo, &paddr, true);
+ if (ret) {
+ dev_err(dev->dev, "could not map (paddr)!\n");
ret = -ENOMEM;
goto fail;
}
@@ -206,18 +224,15 @@ static int omap_fbdev_create(struct drm_fb_helper *helper,
goto fail_unlock;
}
- drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
- size = omap_framebuffer_get_buffer(fb, 0, 0,
- &vaddr, &paddr, &screen_width);
-
dev->mode_config.fb_base = paddr;
- fbi->screen_base = vaddr;
- fbi->screen_size = size;
+ fbi->screen_base = omap_gem_vaddr(fbdev->bo);
+ fbi->screen_size = fbdev->bo->size;
fbi->fix.smem_start = paddr;
- fbi->fix.smem_len = size;
+ fbi->fix.smem_len = fbdev->bo->size;
/* if we have DMM, then we can use it for scrolling by just
* shuffling pages around in DMM rather than doing sw blit.
@@ -362,11 +377,11 @@ void omap_fbdev_free(struct drm_device *dev)
fbdev = to_omap_fbdev(priv->fbdev);
- kfree(fbdev);
-
/* this will free the backing object */
if (fbdev->fb)
fbdev->fb->funcs->destroy(fbdev->fb);
+ kfree(fbdev);
+
priv->fbdev = NULL;
}
diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c
index e0ebd1d139f6..b7d6f886c5cf 100644
--- a/drivers/staging/omapdrm/omap_gem.c
+++ b/drivers/staging/omapdrm/omap_gem.c
@@ -116,6 +116,9 @@ struct omap_gem_object {
} *sync;
};
+static int get_pages(struct drm_gem_object *obj, struct page ***pages);
+static uint64_t mmap_offset(struct drm_gem_object *obj);
+
/* To deal with userspace mmap'ings of 2d tiled buffers, which (a) are
* not necessarily pinned in TILER all the time, and (b) when they are
* they are not necessarily page aligned, we reserve one or more small
@@ -149,7 +152,7 @@ static void evict_entry(struct drm_gem_object *obj,
{
if (obj->dev->dev_mapping) {
size_t size = PAGE_SIZE * usergart[fmt].height;
- loff_t off = omap_gem_mmap_offset(obj) +
+ loff_t off = mmap_offset(obj) +
(entry->obj_pgoff << PAGE_SHIFT);
unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
}
@@ -189,8 +192,6 @@ static inline bool is_shmem(struct drm_gem_object *obj)
return obj->filp != NULL;
}
-static int get_pages(struct drm_gem_object *obj, struct page ***pages);
-
static DEFINE_SPINLOCK(sync_lock);
/** ensure backing pages are allocated */
@@ -251,7 +252,7 @@ static void omap_gem_detach_pages(struct drm_gem_object *obj)
}
/** get mmap offset */
-uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
+static uint64_t mmap_offset(struct drm_gem_object *obj)
{
if (!obj->map_list.map) {
/* Make it mmapable */
@@ -267,6 +268,15 @@ uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
}
+uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
+{
+ uint64_t offset;
+ mutex_lock(&obj->dev->struct_mutex);
+ offset = mmap_offset(obj);
+ mutex_unlock(&obj->dev->struct_mutex);
+ return offset;
+}
+
/** get mmap size */
size_t omap_gem_mmap_size(struct drm_gem_object *obj)
{
@@ -1034,6 +1044,11 @@ void omap_gem_free_object(struct drm_gem_object *obj)
drm_gem_free_mmap_offset(obj);
}
+ /* this means the object is still pinned.. which really should
+ * not happen. I think..
+ */
+ WARN_ON(omap_obj->paddr_cnt > 0);
+
/* don't free externally allocated backing memory */
if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
if (omap_obj->pages) {
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
new file mode 100644
index 000000000000..97909124a1fe
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -0,0 +1,344 @@
+/*
+ * drivers/staging/omapdrm/omap_plane.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * 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 "omap_drv.h"
+
+/* some hackery because omapdss has an 'enum omap_plane' (which would be
+ * better named omap_plane_id).. and compiler seems unhappy about having
+ * both a 'struct omap_plane' and 'enum omap_plane'
+ */
+#define omap_plane _omap_plane
+
+/*
+ * plane funcs
+ */
+
+#define to_omap_plane(x) container_of(x, struct omap_plane, base)
+
+struct omap_plane {
+ struct drm_plane base;
+ struct omap_overlay *ovl;
+ struct omap_overlay_info info;
+
+ /* Source values, converted to integers because we don't support
+ * fractional positions:
+ */
+ unsigned int src_x, src_y;
+
+ /* last fb that we pinned: */
+ struct drm_framebuffer *pinned_fb;
+};
+
+
+/* push changes down to dss2 */
+static int commit(struct drm_plane *plane)
+{
+ struct drm_device *dev = plane->dev;
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_overlay *ovl = omap_plane->ovl;
+ struct omap_overlay_info *info = &omap_plane->info;
+ int ret;
+
+ DBG("%s", ovl->name);
+ DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
+ info->out_height, info->screen_width);
+ DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
+ info->paddr, info->p_uv_addr);
+
+ /* 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 settings\n");
+ return ret;
+ }
+ }
+
+ if (ovl->is_enabled(ovl)) {
+ omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
+ info->out_width, info->out_height);
+ }
+
+ return 0;
+}
+
+/* when CRTC that we are attached to has potentially changed, this checks
+ * if we are attached to proper manager, and if necessary updates.
+ */
+static void update_manager(struct drm_plane *plane)
+{
+ struct omap_drm_private *priv = plane->dev->dev_private;
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_overlay *ovl = omap_plane->ovl;
+ struct omap_overlay_manager *mgr = NULL;
+ int i;
+
+ if (plane->crtc) {
+ for (i = 0; i < priv->num_encoders; i++) {
+ struct drm_encoder *encoder = priv->encoders[i];
+ if (encoder->crtc == plane->crtc) {
+ mgr = omap_encoder_get_manager(encoder);
+ break;
+ }
+ }
+ }
+
+ if (ovl->manager != mgr) {
+ bool enabled = ovl->is_enabled(ovl);
+
+ /* don't switch things around with enabled overlays: */
+ if (enabled)
+ omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+
+ if (ovl->manager) {
+ DBG("disconnecting %s from %s", ovl->name,
+ ovl->manager->name);
+ ovl->unset_manager(ovl);
+ }
+
+ if (mgr) {
+ DBG("connecting %s to %s", ovl->name, mgr->name);
+ ovl->set_manager(ovl, mgr);
+ }
+
+ if (enabled && mgr)
+ omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+ }
+}
+
+/* update which fb (if any) is pinned for scanout */
+static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ int ret = 0;
+
+ if (omap_plane->pinned_fb != fb) {
+ if (omap_plane->pinned_fb)
+ omap_framebuffer_unpin(omap_plane->pinned_fb);
+ omap_plane->pinned_fb = fb;
+ if (fb)
+ ret = omap_framebuffer_pin(fb);
+ }
+
+ return ret;
+}
+
+/* update parameters that are dependent on the framebuffer dimensions and
+ * position within the fb that this plane scans out from. This is called
+ * when framebuffer or x,y base may have changed.
+ */
+static void update_scanout(struct drm_plane *plane)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_overlay_info *info = &omap_plane->info;
+ int ret;
+
+ ret = update_pin(plane, plane->fb);
+ if (ret) {
+ dev_err(plane->dev->dev,
+ "could not pin fb: %d\n", ret);
+ omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+ return;
+ }
+
+ omap_framebuffer_update_scanout(plane->fb,
+ omap_plane->src_x, omap_plane->src_y, info);
+
+ DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
+ omap_plane->src_x, omap_plane->src_y,
+ (u32)info->paddr, (u32)info->p_uv_addr,
+ info->screen_width);
+}
+
+int omap_plane_mode_set(struct drm_plane *plane,
+ struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+
+ /* src values are in Q16 fixed point, convert to integer: */
+ src_x = src_x >> 16;
+ src_y = src_y >> 16;
+ src_w = src_w >> 16;
+ src_h = src_h >> 16;
+
+ omap_plane->info.pos_x = crtc_x;
+ omap_plane->info.pos_y = crtc_y;
+ omap_plane->info.out_width = crtc_w;
+ omap_plane->info.out_height = crtc_h;
+ omap_plane->info.width = src_w;
+ omap_plane->info.height = src_h;
+ omap_plane->src_x = src_x;
+ omap_plane->src_y = src_y;
+
+ /* note: this is done after this fxn returns.. but if we need
+ * to do a commit/update_scanout, etc before this returns we
+ * need the current value.
+ */
+ plane->fb = fb;
+ plane->crtc = crtc;
+
+ update_scanout(plane);
+ update_manager(plane);
+
+ return 0;
+}
+
+static int omap_plane_update(struct drm_plane *plane,
+ struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h,
+ src_x, src_y, src_w, src_h);
+ return omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+}
+
+static int omap_plane_disable(struct drm_plane *plane)
+{
+ return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_plane_destroy(struct drm_plane *plane)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ DBG("%s", omap_plane->ovl->name);
+ omap_plane_disable(plane);
+ drm_plane_cleanup(plane);
+ kfree(omap_plane);
+}
+
+int omap_plane_dpms(struct drm_plane *plane, int mode)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_overlay *ovl = omap_plane->ovl;
+ int r;
+
+ DBG("%s: %d", omap_plane->ovl->name, mode);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ update_scanout(plane);
+ r = commit(plane);
+ if (!r)
+ r = ovl->enable(ovl);
+ } else {
+ r = ovl->disable(ovl);
+ update_pin(plane, NULL);
+ }
+
+ return r;
+}
+
+static const struct drm_plane_funcs omap_plane_funcs = {
+ .update_plane = omap_plane_update,
+ .disable_plane = omap_plane_disable,
+ .destroy = omap_plane_destroy,
+};
+
+static const uint32_t formats[] = {
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_RGBX4444,
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_RGBA4444,
+ DRM_FORMAT_ABGR4444,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_UYVY,
+};
+
+/* initialize plane */
+struct drm_plane *omap_plane_init(struct drm_device *dev,
+ struct omap_overlay *ovl, unsigned int possible_crtcs,
+ bool priv)
+{
+ struct drm_plane *plane = NULL;
+ struct omap_plane *omap_plane;
+
+ DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name,
+ possible_crtcs, priv);
+
+ omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
+ if (!omap_plane) {
+ dev_err(dev->dev, "could not allocate plane\n");
+ goto fail;
+ }
+
+ omap_plane->ovl = ovl;
+ plane = &omap_plane->base;
+
+ drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
+ formats, ARRAY_SIZE(formats), priv);
+
+ /* get our starting configuration, set defaults for parameters
+ * we don't currently use, etc:
+ */
+ ovl->get_overlay_info(ovl, &omap_plane->info);
+ omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA;
+ omap_plane->info.rotation = OMAP_DSS_ROT_0;
+ omap_plane->info.global_alpha = 0xff;
+ omap_plane->info.mirror = 0;
+ omap_plane->info.mirror = 0;
+
+ /* Set defaults depending on whether we are a CRTC or overlay
+ * layer.
+ * TODO add ioctl to give userspace an API to change this.. this
+ * will come in a subsequent patch.
+ */
+ if (priv)
+ omap_plane->info.zorder = 0;
+ else
+ omap_plane->info.zorder = 1;
+
+ update_manager(plane);
+
+ return plane;
+
+fail:
+ if (plane) {
+ omap_plane_destroy(plane);
+ }
+ return NULL;
+}
diff --git a/drivers/staging/omapdrm/omap_priv.h b/drivers/staging/omapdrm/omap_priv.h
index c324709aa9a1..ef6441447147 100644
--- a/drivers/staging/omapdrm/omap_priv.h
+++ b/drivers/staging/omapdrm/omap_priv.h
@@ -27,14 +27,22 @@
* pipes/overlays/CRTCs are used.. if this is not provided, then instead the
* first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to
* one manager, with priority given to managers that are connected to
- * detected devices. This should be a good default behavior for most cases,
- * but yet there still might be times when you wish to do something different.
+ * detected devices. Remaining overlays are used as video planes. This
+ * should be a good default behavior for most cases, but yet there still
+ * might be times when you wish to do something different.
*/
struct omap_kms_platform_data {
+ /* overlays to use as CRTCs: */
int ovl_cnt;
const int *ovl_ids;
+
+ /* overlays to use as video planes: */
+ int pln_cnt;
+ const int *pln_ids;
+
int mgr_cnt;
const int *mgr_ids;
+
int dev_cnt;
const char **dev_names;
};