staging: drm/omap: multiplanar and YUV support
Rob Clark [Mon, 16 Jan 2012 18:51:17 +0000 (12:51 -0600)]
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

drivers/staging/omapdrm/omap_crtc.c
drivers/staging/omapdrm/omap_drv.h
drivers/staging/omapdrm/omap_fb.c
drivers/staging/omapdrm/omap_gem.c
drivers/staging/omapdrm/omap_plane.c

index a91c788..3cee04e 100644 (file)
@@ -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;
index e2b1b55..48f6fce 100644 (file)
@@ -90,9 +90,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,
index 805a18e..d021a7e 100644 (file)
@@ -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 = 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);
@@ -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->pitches[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;
+       int ret, i, n = drm_format_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->pitches[0] / 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 = drm_format_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 >= 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
@@ -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 = drm_format_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);
index e0ebd1d..ae1ad35 100644 (file)
@@ -1034,6 +1034,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) {
index a1d948c..66eed75 100644 (file)
@@ -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;