aboutsummaryrefslogtreecommitdiffstats
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.c222
-rw-r--r--drivers/staging/omapdrm/omap_debugfs.c97
-rw-r--r--drivers/staging/omapdrm/omap_dmm_tiler.c91
-rw-r--r--drivers/staging/omapdrm/omap_dmm_tiler.h15
-rw-r--r--drivers/staging/omapdrm/omap_drv.c97
-rw-r--r--drivers/staging/omapdrm/omap_drv.h67
-rw-r--r--drivers/staging/omapdrm/omap_fb.c270
-rw-r--r--drivers/staging/omapdrm/omap_fbdev.c85
-rw-r--r--drivers/staging/omapdrm/omap_gem.c195
-rw-r--r--drivers/staging/omapdrm/omap_gem_helpers.c2
-rw-r--r--drivers/staging/omapdrm/omap_plane.c487
-rw-r--r--drivers/staging/omapdrm/omap_priv.h12
13 files changed, 1284 insertions, 357 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..490a7f15604b 100644
--- a/drivers/staging/omapdrm/omap_crtc.c
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -27,220 +27,126 @@
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)
+static void vblank_cb(void *arg)
{
+ static uint32_t sequence = 0;
struct drm_crtc *crtc = 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 timeval now;
unsigned long flags;
+ struct timeval now;
WARN_ON(!event);
omap_crtc->event = NULL;
- update_scanout(crtc);
- WARN_ON(commit(crtc));
-
/* wakeup userspace */
- /* TODO: this should happen *after* flip in vsync IRQ handler */
if (event) {
+ do_gettimeofday(&now);
+
spin_lock_irqsave(&dev->event_lock, flags);
+ /* TODO: we can't yet use the vblank time accounting,
+ * because omapdss lower layer is the one that knows
+ * the irq # and registers the handler, which more or
+ * less defeats how drm_irq works.. for now just fake
+ * the sequence number and use gettimeofday..
+ *
event->event.sequence = drm_vblank_count_and_time(
dev, omap_crtc->id, &now);
+ */
+ event->event.sequence = sequence++;
event->event.tv_sec = now.tv_sec;
event->event.tv_usec = now.tv_usec;
list_add_tail(&event->base.link,
@@ -250,6 +156,23 @@ static void page_flip_cb(void *arg)
}
}
+static void page_flip_cb(void *arg)
+{
+ struct drm_crtc *crtc = arg;
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_framebuffer *old_fb = omap_crtc->old_fb;
+
+ omap_crtc->old_fb = NULL;
+
+ omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
+
+ /* really we'd like to setup the callback atomically w/ setting the
+ * new scanout buffer to avoid getting stuck waiting an extra vblank
+ * cycle.. for now go for correctness and later figure out speed..
+ */
+ omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc);
+}
+
static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event)
@@ -264,10 +187,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 +214,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 +228,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_debugfs.c b/drivers/staging/omapdrm/omap_debugfs.c
index da920dfdc59c..2f122e00b51d 100644
--- a/drivers/staging/omapdrm/omap_debugfs.c
+++ b/drivers/staging/omapdrm/omap_debugfs.c
@@ -20,23 +20,118 @@
#include "omap_drv.h"
#include "omap_dmm_tiler.h"
+#include "drm_fb_helper.h"
+
+
#ifdef CONFIG_DEBUG_FS
+static int gem_show(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct omap_drm_private *priv = dev->dev_private;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ seq_printf(m, "All Objects:\n");
+ omap_gem_describe_objects(&priv->obj_list, m);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static int mm_show(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ return drm_mm_dump_table(m, dev->mm_private);
+}
+
+static int fb_show(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct omap_drm_private *priv = dev->dev_private;
+ struct drm_framebuffer *fb;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+ if (ret)
+ return ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret) {
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+ }
+
+ seq_printf(m, "fbcon ");
+ omap_framebuffer_describe(priv->fbdev->fb, m);
+
+ list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
+ if (fb == priv->fbdev->fb)
+ continue;
+
+ seq_printf(m, "user ");
+ omap_framebuffer_describe(fb, m);
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 0;
+}
+
+/* list of debufs files that are applicable to all devices */
static struct drm_info_list omap_debugfs_list[] = {
+ {"gem", gem_show, 0},
+ {"mm", mm_show, 0},
+ {"fb", fb_show, 0},
+};
+
+/* list of debugfs files that are specific to devices with dmm/tiler */
+static struct drm_info_list omap_dmm_debugfs_list[] = {
{"tiler_map", tiler_map_show, 0},
};
int omap_debugfs_init(struct drm_minor *minor)
{
- return drm_debugfs_create_files(omap_debugfs_list,
+ struct drm_device *dev = minor->dev;
+ int ret;
+
+ ret = drm_debugfs_create_files(omap_debugfs_list,
ARRAY_SIZE(omap_debugfs_list),
minor->debugfs_root, minor);
+
+ if (ret) {
+ dev_err(dev->dev, "could not install omap_debugfs_list\n");
+ return ret;
+ }
+
+ if (dmm_is_available())
+ ret = drm_debugfs_create_files(omap_dmm_debugfs_list,
+ ARRAY_SIZE(omap_dmm_debugfs_list),
+ minor->debugfs_root, minor);
+
+ if (ret) {
+ dev_err(dev->dev, "could not install omap_dmm_debugfs_list\n");
+ return ret;
+ }
+
+ return ret;
}
void omap_debugfs_cleanup(struct drm_minor *minor)
{
drm_debugfs_remove_files(omap_debugfs_list,
ARRAY_SIZE(omap_debugfs_list), minor);
+ if (dmm_is_available())
+ drm_debugfs_remove_files(omap_dmm_debugfs_list,
+ ARRAY_SIZE(omap_dmm_debugfs_list), minor);
}
#endif
diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c
index 852d9440f725..1ecb6a73d790 100644
--- a/drivers/staging/omapdrm/omap_dmm_tiler.c
+++ b/drivers/staging/omapdrm/omap_dmm_tiler.c
@@ -34,6 +34,8 @@
#include "omap_dmm_tiler.h"
#include "omap_dmm_priv.h"
+#define DMM_DRIVER_NAME "dmm"
+
/* mappings for associating views to luts */
static struct tcm *containers[TILFMT_NFORMATS];
static struct dmm *omap_dmm;
@@ -465,7 +467,12 @@ size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h)
return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h;
}
-int omap_dmm_remove(void)
+bool dmm_is_initialized(void)
+{
+ return omap_dmm ? true : false;
+}
+
+static int omap_dmm_remove(struct platform_device *dev)
{
struct tiler_block *block, *_block;
int i;
@@ -499,40 +506,49 @@ int omap_dmm_remove(void)
if (omap_dmm->irq != -1)
free_irq(omap_dmm->irq, omap_dmm);
+ iounmap(omap_dmm->base);
kfree(omap_dmm);
+ omap_dmm = NULL;
}
return 0;
}
-int omap_dmm_init(struct drm_device *dev)
+static int omap_dmm_probe(struct platform_device *dev)
{
int ret = -EFAULT, i;
struct tcm_area area = {0};
u32 hwinfo, pat_geom, lut_table_size;
- struct omap_drm_platform_data *pdata = dev->dev->platform_data;
-
- if (!pdata || !pdata->dmm_pdata) {
- dev_err(dev->dev, "dmm platform data not present, skipping\n");
- return ret;
- }
+ struct resource *mem;
omap_dmm = kzalloc(sizeof(*omap_dmm), GFP_KERNEL);
if (!omap_dmm) {
- dev_err(dev->dev, "failed to allocate driver data section\n");
+ dev_err(&dev->dev, "failed to allocate driver data section\n");
goto fail;
}
/* lookup hwmod data - base address and irq */
- omap_dmm->base = pdata->dmm_pdata->base;
- omap_dmm->irq = pdata->dmm_pdata->irq;
- omap_dmm->dev = dev->dev;
+ mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&dev->dev, "failed to get base address resource\n");
+ goto fail;
+ }
+
+ omap_dmm->base = ioremap(mem->start, SZ_2K);
if (!omap_dmm->base) {
- dev_err(dev->dev, "failed to get dmm base address\n");
+ dev_err(&dev->dev, "failed to get dmm base address\n");
+ goto fail;
+ }
+
+ omap_dmm->irq = platform_get_irq(dev, 0);
+ if (omap_dmm->irq < 0) {
+ dev_err(&dev->dev, "failed to get IRQ resource\n");
goto fail;
}
+ omap_dmm->dev = &dev->dev;
+
hwinfo = readl(omap_dmm->base + DMM_PAT_HWINFO);
omap_dmm->num_engines = (hwinfo >> 24) & 0x1F;
omap_dmm->num_lut = (hwinfo >> 16) & 0x1F;
@@ -556,7 +572,7 @@ int omap_dmm_init(struct drm_device *dev)
"omap_dmm_irq_handler", omap_dmm);
if (ret) {
- dev_err(dev->dev, "couldn't register IRQ %d, error %d\n",
+ dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n",
omap_dmm->irq, ret);
omap_dmm->irq = -1;
goto fail;
@@ -575,25 +591,30 @@ int omap_dmm_init(struct drm_device *dev)
omap_dmm->lut = vmalloc(lut_table_size * sizeof(*omap_dmm->lut));
if (!omap_dmm->lut) {
- dev_err(dev->dev, "could not allocate lut table\n");
+ dev_err(&dev->dev, "could not allocate lut table\n");
ret = -ENOMEM;
goto fail;
}
omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32);
if (!omap_dmm->dummy_page) {
- dev_err(dev->dev, "could not allocate dummy page\n");
+ dev_err(&dev->dev, "could not allocate dummy page\n");
ret = -ENOMEM;
goto fail;
}
+
+ /* set dma mask for device */
+ /* NOTE: this is a workaround for the hwmod not initializing properly */
+ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
omap_dmm->dummy_pa = page_to_phys(omap_dmm->dummy_page);
/* alloc refill memory */
- omap_dmm->refill_va = dma_alloc_coherent(dev->dev,
+ omap_dmm->refill_va = dma_alloc_coherent(&dev->dev,
REFILL_BUFFER_SIZE * omap_dmm->num_engines,
&omap_dmm->refill_pa, GFP_KERNEL);
if (!omap_dmm->refill_va) {
- dev_err(dev->dev, "could not allocate refill memory\n");
+ dev_err(&dev->dev, "could not allocate refill memory\n");
goto fail;
}
@@ -602,7 +623,7 @@ int omap_dmm_init(struct drm_device *dev)
omap_dmm->num_engines * sizeof(struct refill_engine),
GFP_KERNEL);
if (!omap_dmm->engines) {
- dev_err(dev->dev, "could not allocate engines\n");
+ dev_err(&dev->dev, "could not allocate engines\n");
ret = -ENOMEM;
goto fail;
}
@@ -624,7 +645,7 @@ int omap_dmm_init(struct drm_device *dev)
omap_dmm->tcm = kzalloc(omap_dmm->num_lut * sizeof(*omap_dmm->tcm),
GFP_KERNEL);
if (!omap_dmm->tcm) {
- dev_err(dev->dev, "failed to allocate lut ptrs\n");
+ dev_err(&dev->dev, "failed to allocate lut ptrs\n");
ret = -ENOMEM;
goto fail;
}
@@ -636,7 +657,7 @@ int omap_dmm_init(struct drm_device *dev)
NULL);
if (!omap_dmm->tcm[i]) {
- dev_err(dev->dev, "failed to allocate container\n");
+ dev_err(&dev->dev, "failed to allocate container\n");
ret = -ENOMEM;
goto fail;
}
@@ -676,7 +697,7 @@ int omap_dmm_init(struct drm_device *dev)
return 0;
fail:
- omap_dmm_remove();
+ omap_dmm_remove(dev);
return ret;
}
@@ -766,10 +787,18 @@ int tiler_map_show(struct seq_file *s, void *arg)
const char *a2d = special;
const char *m2dp = m2d, *a2dp = a2d;
char nice[128];
- int h_adj = omap_dmm->lut_height / ydiv;
- int w_adj = omap_dmm->lut_width / xdiv;
+ int h_adj;
+ int w_adj;
unsigned long flags;
+ if (!omap_dmm) {
+ /* early return if dmm/tiler device is not initialized */
+ return 0;
+ }
+
+ h_adj = omap_dmm->lut_height / ydiv;
+ w_adj = omap_dmm->lut_width / xdiv;
+
map = kzalloc(h_adj * sizeof(*map), GFP_KERNEL);
global_map = kzalloc((w_adj + 1) * h_adj, GFP_KERNEL);
@@ -828,3 +857,17 @@ error:
return 0;
}
#endif
+
+struct platform_driver omap_dmm_driver = {
+ .probe = omap_dmm_probe,
+ .remove = omap_dmm_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DMM_DRIVER_NAME,
+ },
+};
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Andy Gross <andy.gross@ti.com>");
+MODULE_DESCRIPTION("OMAP DMM/Tiler Driver");
+MODULE_ALIAS("platform:" DMM_DRIVER_NAME);
diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.h b/drivers/staging/omapdrm/omap_dmm_tiler.h
index f87cb657d683..7b1052a329e4 100644
--- a/drivers/staging/omapdrm/omap_dmm_tiler.h
+++ b/drivers/staging/omapdrm/omap_dmm_tiler.h
@@ -16,6 +16,7 @@
#ifndef OMAP_DMM_TILER_H
#define OMAP_DMM_TILER_H
+#include <plat/cpu.h>
#include "omap_drv.h"
#include "tcm.h"
@@ -72,10 +73,6 @@ struct tiler_block {
#define TIL_ADDR(x, orient, a)\
((u32) (x) | (orient) | ((a) << SHIFT_ACC_MODE))
-/* externally accessible functions */
-int omap_dmm_init(struct drm_device *dev);
-int omap_dmm_remove(void);
-
#ifdef CONFIG_DEBUG_FS
int tiler_map_show(struct seq_file *s, void *arg);
#endif
@@ -97,7 +94,9 @@ uint32_t tiler_stride(enum tiler_fmt fmt);
size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
+bool dmm_is_initialized(void);
+extern struct platform_driver omap_dmm_driver;
/* GEM bo flags -> tiler fmt */
static inline enum tiler_fmt gem2fmt(uint32_t flags)
@@ -127,9 +126,9 @@ static inline bool validfmt(enum tiler_fmt fmt)
}
}
-struct omap_dmm_platform_data {
- void __iomem *base;
- int irq;
-};
+static inline int dmm_is_available(void)
+{
+ return cpu_is_omap44xx();
+}
#endif
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
index 602aa2dd49c8..3df5b4c58ecd 100644
--- a/drivers/staging/omapdrm/omap_drv.c
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -21,6 +21,7 @@
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
+#include "omap_dmm_tiler.h"
#define DRIVER_NAME MODULE_NAME
#define DRIVER_DESC "OMAP DRM"
@@ -204,12 +205,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 +240,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 +255,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 +342,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 +369,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 +383,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 +391,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..
@@ -557,6 +571,11 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = priv;
+ priv->wq = alloc_workqueue("omapdrm",
+ WQ_UNBOUND | WQ_NON_REENTRANT, 1);
+
+ INIT_LIST_HEAD(&priv->obj_list);
+
omap_gem_init(dev);
ret = omap_modeset_init(dev);
@@ -585,6 +604,8 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
static int dev_unload(struct drm_device *dev)
{
+ struct omap_drm_private *priv = dev->dev_private;
+
DBG("unload: dev=%p", dev);
drm_vblank_cleanup(dev);
@@ -594,6 +615,9 @@ static int dev_unload(struct drm_device *dev)
omap_modeset_free(dev);
omap_gem_deinit(dev);
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
kfree(dev->dev_private);
dev->dev_private = NULL;
@@ -708,6 +732,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 +774,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,
@@ -777,6 +803,9 @@ static void pdev_shutdown(struct platform_device *device)
static int pdev_probe(struct platform_device *device)
{
DBG("%s", device->name);
+ if (platform_driver_register(&omap_dmm_driver))
+ dev_err(&device->dev, "DMM registration failed\n");
+
return drm_platform_init(&omap_drm_driver, device);
}
@@ -784,6 +813,8 @@ static int pdev_remove(struct platform_device *device)
{
DBG("");
drm_platform_exit(&omap_drm_driver, device);
+
+ platform_driver_unregister(&omap_dmm_driver);
return 0;
}
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
index 76c42515ecc5..b7e0f0773003 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,19 +42,31 @@
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;
struct drm_connector *connectors[8];
struct drm_fb_helper *fbdev;
+ struct workqueue_struct *wq;
+
+ struct list_head obj_list;
+
bool has_dmm;
};
#ifdef CONFIG_DEBUG_FS
int omap_debugfs_init(struct drm_minor *minor);
void omap_debugfs_cleanup(struct drm_minor *minor);
+void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
+void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
+void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
#endif
struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev);
@@ -61,7 +74,19 @@ 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);
+void omap_plane_on_endwin(struct drm_plane *plane,
+ void (*fxn)(void *), void *arg);
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
struct omap_overlay_manager *mgr);
@@ -79,13 +104,18 @@ void omap_connector_mode_set(struct drm_connector *connector,
void omap_connector_flush(struct drm_connector *connector,
int x, int y, int w, int h);
+uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
+ uint32_t max_formats, enum omap_color_mode supported_modes);
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_replace(struct drm_framebuffer *a,
+ struct drm_framebuffer *b, void *arg,
+ void (*unpin)(void *arg, struct drm_gem_object *bo));
+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 +162,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..04b235b6724a 100644
--- a/drivers/staging/omapdrm/omap_fb.c
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -22,18 +22,71 @@
#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 },
+};
+
+/* convert from overlay's pixel formats bitmask to an array of fourcc's */
+uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
+ uint32_t max_formats, enum omap_color_mode supported_modes)
+{
+ uint32_t nformats = 0;
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats) && nformats < max_formats; i++)
+ if (formats[i].dss_format & supported_modes)
+ pixel_formats[nformats++] = formats[i].pixel_format;
+
+ return nformats;
+}
+
+/* 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 +94,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(fb->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 +137,90 @@ 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)
+/* 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);
- int bpp = fb->bits_per_pixel / 8;
- unsigned long offset;
+ 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;
+ }
+}
+
+/* Call for unpin 'a' (if not NULL), and pin 'b' (if not NULL). Although
+ * buffers to unpin are just just pushed to the unpin fifo so that the
+ * caller can defer unpin until vblank.
+ *
+ * Note if this fails (ie. something went very wrong!), all buffers are
+ * unpinned, and the caller disables the overlay. We could have tried
+ * to revert back to the previous set of pinned buffers but if things are
+ * hosed there is no guarantee that would succeed.
+ */
+int omap_framebuffer_replace(struct drm_framebuffer *a,
+ struct drm_framebuffer *b, void *arg,
+ void (*unpin)(void *arg, struct drm_gem_object *bo))
+{
+ int ret = 0, i, na, nb;
+ struct omap_framebuffer *ofba = to_omap_framebuffer(a);
+ struct omap_framebuffer *ofbb = to_omap_framebuffer(b);
+
+ na = a ? drm_format_num_planes(a->pixel_format) : 0;
+ nb = b ? drm_format_num_planes(b->pixel_format) : 0;
+
+ for (i = 0; i < max(na, nb); i++) {
+ struct plane *pa, *pb;
+
+ pa = (i < na) ? &ofba->planes[i] : NULL;
+ pb = (i < nb) ? &ofbb->planes[i] : NULL;
- offset = (x * bpp) + (y * fb->pitch);
+ if (pa) {
+ unpin(arg, pa->bo);
+ pa->paddr = 0;
+ }
- 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;
+ if (pb && !ret)
+ ret = omap_gem_get_paddr(pb->bo, &pb->paddr, true);
}
- *paddr = omap_fb->paddr + offset;
- *screen_width = fb->pitch / bpp;
+ if (ret) {
+ /* something went wrong.. unpin what has been pinned */
+ for (i = 0; i < nb; i++) {
+ struct plane *pb = &ofba->planes[i];
+ if (pb->paddr) {
+ unpin(arg, pb->bo);
+ pb->paddr = 0;
+ }
+ }
+ }
- return omap_fb->size - offset;
+ return ret;
}
-struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb)
+struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
- return omap_fb->bo;
+ if (p >= drm_format_num_planes(fb->pixel_format))
+ return NULL;
+ return omap_fb->planes[p].bo;
}
/* iterate thru all the connectors, returning ones that are attached
@@ -170,40 +277,76 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
}
}
+#ifdef CONFIG_DEBUG_FS
+void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
+{
+ struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+ int i, n = drm_format_num_planes(fb->pixel_format);
+
+ seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height,
+ (char *)&fb->pixel_format);
+
+ for (i = 0; i < n; i++) {
+ struct plane *plane = &omap_fb->planes[i];
+ seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
+ i, plane->offset, plane->pitch);
+ omap_gem_describe(plane->bo, m);
+ }
+}
+#endif
+
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);
- /* 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);
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].pixel_format == mode_cmd->pixel_format) {
+ format = &formats[i];
+ break;
+ }
+ }
+
+ 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 +359,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];
- omap_fb->bo = bo;
- omap_fb->size = size;
+ 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;
+ }
- if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) {
- dev_err(dev->dev, "could not map (paddr)!\n");
- goto fail;
+ size = pitch * mode_cmd->height / format->planes[i].sub_y;
+
+ 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 = pitch;
+ plane->paddr = 0;
}
drm_helper_mode_fill_fb_struct(fb, mode_cmd);
@@ -239,5 +395,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..11acd4c35ed2 100644
--- a/drivers/staging/omapdrm/omap_fbdev.c
+++ b/drivers/staging/omapdrm/omap_fbdev.c
@@ -37,6 +37,9 @@ struct omap_fbdev {
struct drm_framebuffer *fb;
struct drm_gem_object *bo;
bool ywrap_enabled;
+
+ /* for deferred dmm roll when getting called in atomic ctx */
+ struct work_struct work;
};
static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
@@ -75,12 +78,22 @@ static void omap_fbdev_imageblit(struct fb_info *fbi,
image->width, image->height);
}
+static void pan_worker(struct work_struct *work)
+{
+ struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
+ struct fb_info *fbi = fbdev->base.fbdev;
+ int npages;
+
+ /* DMM roll shifts in 4K pages: */
+ npages = fbi->fix.line_length >> PAGE_SHIFT;
+ omap_gem_roll(fbdev->bo, fbi->var.yoffset * npages);
+}
+
static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
struct fb_info *fbi)
{
struct drm_fb_helper *helper = get_fb(fbi);
struct omap_fbdev *fbdev = to_omap_fbdev(helper);
- int npages;
if (!helper)
goto fallback;
@@ -88,9 +101,12 @@ static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
if (!fbdev->ywrap_enabled)
goto fallback;
- /* DMM roll shifts in 4K pages: */
- npages = fbi->fix.line_length >> PAGE_SHIFT;
- omap_gem_roll(fbdev->bo, var->yoffset * npages);
+ if (drm_can_sleep()) {
+ pan_worker(&fbdev->work);
+ } else {
+ struct omap_drm_private *priv = helper->dev->dev_private;
+ queue_work(priv->wq, &fbdev->work);
+ }
return 0;
@@ -129,10 +145,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 +159,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 +240,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.
@@ -321,6 +352,8 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
goto fail;
}
+ INIT_WORK(&fbdev->work, pan_worker);
+
helper = &fbdev->base;
helper->funcs = &omap_fb_helper_funcs;
@@ -362,11 +395,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..921f058cc6a4 100644
--- a/drivers/staging/omapdrm/omap_gem.c
+++ b/drivers/staging/omapdrm/omap_gem.c
@@ -45,6 +45,8 @@ int _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size);
struct omap_gem_object {
struct drm_gem_object base;
+ struct list_head mm_list;
+
uint32_t flags;
/** width/height for tiled formats (rounded up to slot boundaries) */
@@ -116,6 +118,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
@@ -148,10 +153,23 @@ static void evict_entry(struct drm_gem_object *obj,
enum tiler_fmt fmt, struct usergart_entry *entry)
{
if (obj->dev->dev_mapping) {
- size_t size = PAGE_SIZE * usergart[fmt].height;
- loff_t off = omap_gem_mmap_offset(obj) +
+ struct omap_gem_object *omap_obj = to_omap_bo(obj);
+ int n = usergart[fmt].height;
+ size_t size = PAGE_SIZE * n;
+ loff_t off = mmap_offset(obj) +
(entry->obj_pgoff << PAGE_SHIFT);
- unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
+ const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
+ if (m > 1) {
+ int i;
+ /* if stride > than PAGE_SIZE then sparse mapping: */
+ for (i = n; i > 0; i--) {
+ unmap_mapping_range(obj->dev->dev_mapping,
+ off, PAGE_SIZE, 1);
+ off += PAGE_SIZE * m;
+ }
+ } else {
+ unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
+ }
}
entry->obj = NULL;
@@ -189,8 +207,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,15 +267,19 @@ 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)
{
+ struct drm_device *dev = obj->dev;
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
if (!obj->map_list.map) {
/* Make it mmapable */
size_t size = omap_gem_mmap_size(obj);
int ret = _drm_gem_create_mmap_offset_size(obj, size);
if (ret) {
- dev_err(obj->dev->dev, "could not allocate mmap offset");
+ dev_err(dev->dev, "could not allocate mmap offset\n");
return 0;
}
}
@@ -267,6 +287,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)
{
@@ -326,26 +355,39 @@ static int fault_2d(struct drm_gem_object *obj,
void __user *vaddr;
int i, ret, slots;
- if (!usergart)
- return -EFAULT;
-
- /* TODO: this fxn might need a bit tweaking to deal w/ tiled buffers
- * that are wider than 4kb
+ /*
+ * Note the height of the slot is also equal to the number of pages
+ * that need to be mapped in to fill 4kb wide CPU page. If the slot
+ * height is 64, then 64 pages fill a 4kb wide by 64 row region.
+ */
+ const int n = usergart[fmt].height;
+ const int n_shift = usergart[fmt].height_shift;
+
+ /*
+ * If buffer width in bytes > PAGE_SIZE then the virtual stride is
+ * rounded up to next multiple of PAGE_SIZE.. this need to be taken
+ * into account in some of the math, so figure out virtual stride
+ * in pages
*/
+ const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
/* We don't use vmf->pgoff since that has the fake offset: */
pgoff = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
- /* actual address we start mapping at is rounded down to previous slot
+ /*
+ * Actual address we start mapping at is rounded down to previous slot
* boundary in the y direction:
*/
- base_pgoff = round_down(pgoff, usergart[fmt].height);
- vaddr = vmf->virtual_address - ((pgoff - base_pgoff) << PAGE_SHIFT);
- entry = &usergart[fmt].entry[usergart[fmt].last];
+ base_pgoff = round_down(pgoff, m << n_shift);
+ /* figure out buffer width in slots */
slots = omap_obj->width >> usergart[fmt].slot_shift;
+ vaddr = vmf->virtual_address - ((pgoff - base_pgoff) << PAGE_SHIFT);
+
+ entry = &usergart[fmt].entry[usergart[fmt].last];
+
/* evict previous buffer using this usergart entry, if any: */
if (entry->obj)
evict_entry(entry->obj, fmt, entry);
@@ -353,23 +395,30 @@ static int fault_2d(struct drm_gem_object *obj,
entry->obj = obj;
entry->obj_pgoff = base_pgoff;
- /* now convert base_pgoff to phys offset from virt offset:
- */
- base_pgoff = (base_pgoff >> usergart[fmt].height_shift) * slots;
-
- /* map in pages. Note the height of the slot is also equal to the
- * number of pages that need to be mapped in to fill 4kb wide CPU page.
- * If the height is 64, then 64 pages fill a 4kb wide by 64 row region.
- * Beyond the valid pixel part of the buffer, we set pages[i] to NULL to
- * get a dummy page mapped in.. if someone reads/writes it they will get
- * random/undefined content, but at least it won't be corrupting
- * whatever other random page used to be mapped in, or other undefined
- * behavior.
+ /* now convert base_pgoff to phys offset from virt offset: */
+ base_pgoff = (base_pgoff >> n_shift) * slots;
+
+ /* for wider-than 4k.. figure out which part of the slot-row we want: */
+ if (m > 1) {
+ int off = pgoff % m;
+ entry->obj_pgoff += off;
+ base_pgoff /= m;
+ slots = min(slots - (off << n_shift), n);
+ base_pgoff += off << n_shift;
+ vaddr += off << PAGE_SHIFT;
+ }
+
+ /*
+ * Map in pages. Beyond the valid pixel part of the buffer, we set
+ * pages[i] to NULL to get a dummy page mapped in.. if someone
+ * reads/writes it they will get random/undefined content, but at
+ * least it won't be corrupting whatever other random page used to
+ * be mapped in, or other undefined behavior.
*/
memcpy(pages, &omap_obj->pages[base_pgoff],
sizeof(struct page *) * slots);
memset(pages + slots, 0,
- sizeof(struct page *) * (usergart[fmt].height - slots));
+ sizeof(struct page *) * (n - slots));
ret = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true);
if (ret) {
@@ -377,16 +426,15 @@ static int fault_2d(struct drm_gem_object *obj,
return ret;
}
- i = usergart[fmt].height;
pfn = entry->paddr >> PAGE_SHIFT;
VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
pfn, pfn << PAGE_SHIFT);
- while (i--) {
+ for (i = n; i > 0; i--) {
vm_insert_mixed(vma, (unsigned long)vaddr, pfn);
pfn += usergart[fmt].stride_pfn;
- vaddr += PAGE_SIZE;
+ vaddr += PAGE_SIZE * m;
}
/* simple round-robin: */
@@ -556,6 +604,8 @@ fail:
/* Set scrolling position. This allows us to implement fast scrolling
* for console.
+ *
+ * Call only from non-atomic contexts.
*/
int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll)
{
@@ -570,18 +620,6 @@ int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll)
omap_obj->roll = roll;
- if (in_atomic() || mutex_is_locked(&obj->dev->struct_mutex)) {
- /* this can get called from fbcon in atomic context.. so
- * just ignore it and wait for next time called from
- * interruptible context to update the PAT.. the result
- * may be that user sees wrap-around instead of scrolling
- * momentarily on the screen. If we wanted to be fancier
- * we could perhaps schedule some workqueue work at this
- * point.
- */
- return 0;
- }
-
mutex_lock(&obj->dev->struct_mutex);
/* if we aren't mapped yet, we don't need to do anything */
@@ -764,6 +802,56 @@ void *omap_gem_vaddr(struct drm_gem_object *obj)
return omap_obj->vaddr;
}
+#ifdef CONFIG_DEBUG_FS
+void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
+{
+ struct drm_device *dev = obj->dev;
+ struct omap_gem_object *omap_obj = to_omap_bo(obj);
+ uint64_t off = 0;
+
+ WARN_ON(! mutex_is_locked(&dev->struct_mutex));
+
+ if (obj->map_list.map)
+ off = (uint64_t)obj->map_list.hash.key;
+
+ seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
+ omap_obj->flags, obj->name, obj->refcount.refcount.counter,
+ off, omap_obj->paddr, omap_obj->paddr_cnt,
+ omap_obj->vaddr, omap_obj->roll);
+
+ if (omap_obj->flags & OMAP_BO_TILED) {
+ seq_printf(m, " %dx%d", omap_obj->width, omap_obj->height);
+ if (omap_obj->block) {
+ struct tcm_area *area = &omap_obj->block->area;
+ seq_printf(m, " (%dx%d, %dx%d)",
+ area->p0.x, area->p0.y,
+ area->p1.x, area->p1.y);
+ }
+ } else {
+ seq_printf(m, " %d", obj->size);
+ }
+
+ seq_printf(m, "\n");
+}
+
+void omap_gem_describe_objects(struct list_head *list, struct seq_file *m)
+{
+ struct omap_gem_object *omap_obj;
+ int count = 0;
+ size_t size = 0;
+
+ list_for_each_entry(omap_obj, list, mm_list) {
+ struct drm_gem_object *obj = &omap_obj->base;
+ seq_printf(m, " ");
+ omap_gem_describe(obj, m);
+ count++;
+ size += obj->size;
+ }
+
+ seq_printf(m, "Total %d objects, %zu bytes\n", count, size);
+}
+#endif
+
/* Buffer Synchronization:
*/
@@ -1030,10 +1118,19 @@ void omap_gem_free_object(struct drm_gem_object *obj)
evict(obj);
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ list_del(&omap_obj->mm_list);
+
if (obj->map_list.map) {
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) {
@@ -1125,6 +1222,8 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
goto fail;
}
+ list_add(&omap_obj->mm_list, &priv->obj_list);
+
obj = &omap_obj->base;
if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) {
@@ -1171,12 +1270,11 @@ void omap_gem_init(struct drm_device *dev)
const enum tiler_fmt fmts[] = {
TILFMT_8BIT, TILFMT_16BIT, TILFMT_32BIT
};
- int i, j, ret;
+ int i, j;
- ret = omap_dmm_init(dev);
- if (ret) {
+ if (!dmm_is_initialized()) {
/* DMM only supported on OMAP4 and later, so this isn't fatal */
- dev_warn(dev->dev, "omap_dmm_init failed, disabling DMM\n");
+ dev_warn(dev->dev, "DMM not available, disable DMM support\n");
return;
}
@@ -1226,6 +1324,5 @@ void omap_gem_deinit(struct drm_device *dev)
/* I believe we can rely on there being no more outstanding GEM
* objects which could depend on usergart/dmm at this point.
*/
- omap_dmm_remove();
kfree(usergart);
}
diff --git a/drivers/staging/omapdrm/omap_gem_helpers.c b/drivers/staging/omapdrm/omap_gem_helpers.c
index 29275c7209e9..f895363a5e54 100644
--- a/drivers/staging/omapdrm/omap_gem_helpers.c
+++ b/drivers/staging/omapdrm/omap_gem_helpers.c
@@ -84,7 +84,7 @@ fail:
page_cache_release(pages[i]);
}
drm_free_large(pages);
- return ERR_PTR(PTR_ERR(p));
+ return ERR_CAST(p);
}
/**
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
new file mode 100644
index 000000000000..7997be74010d
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -0,0 +1,487 @@
+/*
+ * 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 <linux/kfifo.h>
+
+#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
+ */
+
+struct callback {
+ void (*fxn)(void *);
+ void *arg;
+};
+
+#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;
+
+ uint32_t nformats;
+ uint32_t formats[32];
+
+ /* for synchronizing access to unpins fifo */
+ struct mutex unpin_mutex;
+
+ /* set of bo's pending unpin until next END_WIN irq */
+ DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
+ int num_unpins, pending_num_unpins;
+
+ /* for deferred unpin when we need to wait for scanout complete irq */
+ struct work_struct work;
+
+ /* callback on next endwin irq */
+ struct callback endwin;
+};
+
+/* map from ovl->id to the irq we are interested in for scanout-done */
+static const uint32_t id2irq[] = {
+ [OMAP_DSS_GFX] = DISPC_IRQ_GFX_END_WIN,
+ [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN,
+ [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN,
+ [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN,
+};
+
+static void dispc_isr(void *arg, uint32_t mask)
+{
+ struct drm_plane *plane = arg;
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_drm_private *priv = plane->dev->dev_private;
+
+ omap_dispc_unregister_isr(dispc_isr, plane,
+ id2irq[omap_plane->ovl->id]);
+
+ queue_work(priv->wq, &omap_plane->work);
+}
+
+static void unpin_worker(struct work_struct *work)
+{
+ struct omap_plane *omap_plane =
+ container_of(work, struct omap_plane, work);
+ struct callback endwin;
+
+ mutex_lock(&omap_plane->unpin_mutex);
+ DBG("unpinning %d of %d", omap_plane->num_unpins,
+ omap_plane->num_unpins + omap_plane->pending_num_unpins);
+ while (omap_plane->num_unpins > 0) {
+ struct drm_gem_object *bo = NULL;
+ int ret = kfifo_get(&omap_plane->unpin_fifo, &bo);
+ WARN_ON(!ret);
+ omap_gem_put_paddr(bo);
+ drm_gem_object_unreference_unlocked(bo);
+ omap_plane->num_unpins--;
+ }
+ endwin = omap_plane->endwin;
+ omap_plane->endwin.fxn = NULL;
+ mutex_unlock(&omap_plane->unpin_mutex);
+
+ if (endwin.fxn)
+ endwin.fxn(endwin.arg);
+}
+
+static void install_irq(struct drm_plane *plane)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_overlay *ovl = omap_plane->ovl;
+ int ret;
+
+ ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]);
+
+ /*
+ * omapdss has upper limit on # of registered irq handlers,
+ * which we shouldn't hit.. but if we do the limit should
+ * be raised or bad things happen:
+ */
+ WARN_ON(ret == -EBUSY);
+}
+
+/* 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;
+ }
+
+ mutex_lock(&omap_plane->unpin_mutex);
+ omap_plane->num_unpins += omap_plane->pending_num_unpins;
+ omap_plane->pending_num_unpins = 0;
+ mutex_unlock(&omap_plane->unpin_mutex);
+
+ /* 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;
+ }
+
+ /*
+ * NOTE: really this should be atomic w/ mgr->apply() but
+ * omapdss does not expose such an API
+ */
+ if (omap_plane->num_unpins > 0)
+ install_irq(plane);
+
+ } else {
+ struct omap_drm_private *priv = dev->dev_private;
+ queue_work(priv->wq, &omap_plane->work);
+ }
+
+
+ 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);
+ }
+}
+
+static void unpin(void *arg, struct drm_gem_object *bo)
+{
+ struct drm_plane *plane = arg;
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+
+ if (kfifo_put(&omap_plane->unpin_fifo,
+ (const struct drm_gem_object **)&bo)) {
+ omap_plane->pending_num_unpins++;
+ /* also hold a ref so it isn't free'd while pinned */
+ drm_gem_object_reference(bo);
+ } else {
+ dev_err(plane->dev->dev, "unpin fifo full!\n");
+ omap_gem_put_paddr(bo);
+ }
+}
+
+/* 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);
+ struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
+
+ if (pinned_fb != fb) {
+ int ret;
+
+ DBG("%p -> %p", pinned_fb, fb);
+
+ mutex_lock(&omap_plane->unpin_mutex);
+ ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
+ mutex_unlock(&omap_plane->unpin_mutex);
+
+ if (ret) {
+ dev_err(plane->dev->dev, "could not swap %p -> %p\n",
+ omap_plane->pinned_fb, fb);
+ omap_plane->pinned_fb = NULL;
+ return ret;
+ }
+
+ omap_plane->pinned_fb = fb;
+ }
+
+ return 0;
+}
+
+/* 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);
+ WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0);
+ kfifo_free(&omap_plane->unpin_fifo);
+ 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 {
+ struct omap_drm_private *priv = plane->dev->dev_private;
+ r = ovl->disable(ovl);
+ update_pin(plane, NULL);
+ queue_work(priv->wq, &omap_plane->work);
+ }
+
+ return r;
+}
+
+void omap_plane_on_endwin(struct drm_plane *plane,
+ void (*fxn)(void *), void *arg)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+
+ mutex_lock(&omap_plane->unpin_mutex);
+ omap_plane->endwin.fxn = fxn;
+ omap_plane->endwin.arg = arg;
+ mutex_unlock(&omap_plane->unpin_mutex);
+
+ install_irq(plane);
+}
+
+static const struct drm_plane_funcs omap_plane_funcs = {
+ .update_plane = omap_plane_update,
+ .disable_plane = omap_plane_disable,
+ .destroy = omap_plane_destroy,
+};
+
+/* 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;
+ int ret;
+
+ DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name,
+ possible_crtcs, priv);
+
+ /* friendly reminder to update table for future hw: */
+ WARN_ON(ovl->id >= ARRAY_SIZE(id2irq));
+
+ omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
+ if (!omap_plane) {
+ dev_err(dev->dev, "could not allocate plane\n");
+ goto fail;
+ }
+
+ mutex_init(&omap_plane->unpin_mutex);
+
+ ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
+ if (ret) {
+ dev_err(dev->dev, "could not allocate unpin FIFO\n");
+ goto fail;
+ }
+
+ INIT_WORK(&omap_plane->work, unpin_worker);
+
+ omap_plane->nformats = omap_framebuffer_get_formats(
+ omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
+ ovl->supported_modes);
+ omap_plane->ovl = ovl;
+ plane = &omap_plane->base;
+
+ drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
+ omap_plane->formats, omap_plane->nformats, 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 = ovl->id;
+
+ 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;
};