[3/3] drm/omap: multiplanar and YUV support

Message ID 1323929782-11217-4-git-send-email-rob.clark@linaro.org
State New
Headers show

Commit Message

Rob Clark Dec. 15, 2011, 6:16 a.m.
From: Rob Clark <rob@ti.com>

Add support in framebuffer objects for other color formats and multi-
planar YUV (NV12).  Since this requires changing the API between the
plane and fb for getting scanout information (paddr, etc), take
advantage of the opportunity and put in place a way to allow fb's to
be unpinned when they are not being scanned out.  Now, before start
of scanout the plane calls omap_framebuffer_pin() which takes care
to pin all the backing bo's, then omap_framebuffer_update_scanout()
however many times to update the scanout address(es), etc, and then
when finished omap_framebuffer_unpin().

Signed-off-by: Rob Clark <rob@ti.com>
---
 drivers/staging/omapdrm/omap_crtc.c  |    2 +-
 drivers/staging/omapdrm/omap_drv.h   |    8 +-
 drivers/staging/omapdrm/omap_fb.c    |  149 +++++++++++++++++++++++-----------
 drivers/staging/omapdrm/omap_gem.c   |    5 +
 drivers/staging/omapdrm/omap_plane.c |   53 +++++++++----
 5 files changed, 149 insertions(+), 68 deletions(-)

Patch

diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
index a91c788..3cee04e 100644
--- a/drivers/staging/omapdrm/omap_crtc.c
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -168,7 +168,7 @@  static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
 	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;
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
index 0394f17..d29d7cb 100644
--- a/drivers/staging/omapdrm/omap_drv.h
+++ b/drivers/staging/omapdrm/omap_drv.h
@@ -85,9 +85,11 @@  struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
 		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_cmd2 *mode_cmd, struct drm_gem_object **bos);
-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_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,
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c
index 0d3a15e..e1f97d8 100644
--- a/drivers/staging/omapdrm/omap_fb.c
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -59,14 +59,20 @@  static const struct format formats[] = {
 	{ 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,
@@ -74,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 = 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);
@@ -116,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;
-
-	offset = (x * bpp) + (y * fb->pitch);
-
-	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;
+	int ret, i, n = num_planes(omap_fb->format->pixel_format);
+
+	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;
 	}
 
-	*paddr = omap_fb->paddr + offset;
-	*screen_width = fb->pitch / bpp;
+	return 0;
 
-	return omap_fb->size - offset;
+fail:
+	while (--i > 0) {
+		struct plane *plane = &omap_fb->planes[i];
+		omap_gem_put_paddr(plane->bo);
+	}
+	return ret;
 }
 
-struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb)
+/* releases buffer when done with scanout */
+void omap_framebuffer_unpin(struct drm_framebuffer *fb)
 {
 	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
-	return omap_fb->bo;
+	int i, n = num_planes(omap_fb->format->pixel_format);
+
+	for (i = 0; i < n; i++) {
+		struct plane *plane = &omap_fb->planes[i];
+		omap_gem_put_paddr(plane->bo);
+	}
+}
+
+/* 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);
+	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 >= 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
@@ -231,7 +277,7 @@  struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
 	struct omap_framebuffer *omap_fb;
 	struct drm_framebuffer *fb = NULL;
 	const struct format *format = NULL;
-	int i, size, ret;
+	int ret, i, n = num_planes(mode_cmd->pixel_format);
 
 	DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
 			dev, mode_cmd, mode_cmd->width, mode_cmd->height,
@@ -251,10 +297,6 @@  struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
 		goto fail;
 	}
 
-	/* in case someone tries to feed us a completely bogus stride: */
-	mode_cmd->pitches[0] = align_pitch(mode_cmd->pitches[0],
-			mode_cmd->width, format->planes[0].stride_bpp);
-
 	omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
 	if (!omap_fb) {
 		dev_err(dev->dev, "could not allocate fb\n");
@@ -271,21 +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->pitches[0] * mode_cmd->height);
+	omap_fb->format = format;
 
-	if (size > bos[0]->size) {
-		dev_err(dev->dev, "provided buffer object is too small!\n");
-		ret = -EINVAL;
-		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 = bos[0];
-	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;
+		}
 
-	ret = omap_gem_get_paddr(bos[0], &omap_fb->paddr, true);
-	if (ret) {
-		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  = mode_cmd->pitches[i];
+		plane->paddr  = pitch;
 	}
 
 	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c
index 9684891..bbec302 100644
--- a/drivers/staging/omapdrm/omap_gem.c
+++ b/drivers/staging/omapdrm/omap_gem.c
@@ -992,6 +992,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
index a1d948c..66eed75 100644
--- a/drivers/staging/omapdrm/omap_plane.c
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -40,6 +40,9 @@  struct omap_plane {
 	 * fractional positions:
 	 */
 	unsigned int src_x, src_y;
+
+	/* last fb that we pinned: */
+	struct drm_framebuffer *pinned_fb;
 };
 
 
@@ -55,7 +58,8 @@  static int commit(struct drm_plane *plane)
 	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", info->pos_x, info->pos_y, info->paddr);
+	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
@@ -133,6 +137,23 @@  static void update_manager(struct drm_plane *plane)
 	}
 }
 
+/* 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.
@@ -140,19 +161,23 @@  static void update_manager(struct drm_plane *plane)
 static void update_scanout(struct drm_plane *plane)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
-	unsigned int screen_width; /* really means "pitch" */
-	dma_addr_t paddr;
+	struct omap_overlay_info *info = &omap_plane->info;
+	int ret;
 
-	omap_framebuffer_get_buffer(plane->fb,
-			omap_plane->src_x, omap_plane->src_y,
-			NULL, &paddr, &screen_width);
+	ret = update_pin(plane, plane->fb);
+	if (ret) {
+		dev_err(plane->dev->dev,
+			"could not pin fb: %d\n", ret);
+		omap_plane->info.enabled = false;
+	}
 
-	DBG("%s: %d,%d: %08x (%d)", omap_plane->ovl->name,
-			omap_plane->src_x, omap_plane->src_y,
-			(u32)paddr, screen_width);
+	omap_framebuffer_update_scanout(plane->fb,
+			omap_plane->src_x, omap_plane->src_y, info);
 
-	omap_plane->info.paddr = paddr;
-	omap_plane->info.screen_width = screen_width;
+	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);
 }
 
 static int omap_plane_update(struct drm_plane *plane,
@@ -219,6 +244,7 @@  int omap_plane_dpms(struct drm_plane *plane, int mode)
 		omap_plane->info.enabled = true;
 	} else {
 		omap_plane->info.enabled = false;
+		update_pin(plane, NULL);
 	}
 
 	return commit(plane);
@@ -291,11 +317,6 @@  struct drm_plane *omap_plane_init(struct drm_device *dev,
 	else
 		omap_plane->info.zorder = 1;
 
-	/* TODO color mode should come from fb.. this will come in a
-	 * subsequent patch
-	 */
-	omap_plane->info.color_mode = OMAP_DSS_COLOR_RGB24U;
-
 	update_manager(plane);
 
 	return plane;