video: tegra: dc: Separate allocations for U and V
Robert Morell [Sat, 16 Jul 2011 01:47:06 +0000 (18:47 -0700)]
Currently, dc_ext only takes a single nvmap memory ID per overlay, even
in the YUV case (the U and V planes are expected to be differentiated
using an offset from the beginning of the nvmap allocation).

This is problematic for some software flows, such as certain video
interlacing algorithms that will vary the luma plane while keeping the
chrome plane constant.

This change allows dc_ext clients to specify a different nvmap
allocation for each of the Y, U, and V planes.  If a YUV surface is
used and no U or V plane allocation is specified, the old behavior is
preserved: the U and V offsets are assumed to be within the same
allocation as Y.

Note: this changes the behavior of the offset parameter: the old code
added offset to offset_u and offset_v when using it.  The new code
treats all three offsets as relative to the beginning of the allocation.
It also fixes a bug in the code where offset was applied twice to the Y
plane.  I believe this is safe because the presence of this bug means
that no existing clients are using offset != 0 (or if they are, they're
already broken).

Signed-off-by: Robert Morell <rmorell@nvidia.com>

Bug 850882

Original-Change-Id: I230e03db25baaae73a3bdc0d45a2aec162b87fa4
Reviewed-on: http://git-master/r/41471
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>

Rebase-Id: Ra6dd17a50de7150edf104d2a6c9b3b9949919022

arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/ext/dev.c
drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
drivers/video/tegra/dc/overlay.c
drivers/video/tegra/fb.c
include/video/tegra_dc_ext.h

index ec24c32..5aac575 100644 (file)
@@ -370,9 +370,8 @@ struct tegra_dc_win {
 
        void                    *virt_addr;
        dma_addr_t              phys_addr;
-       unsigned                offset;
-       unsigned                offset_u;
-       unsigned                offset_v;
+       dma_addr_t              phys_addr_u;
+       dma_addr_t              phys_addr_v;
        unsigned                stride;
        unsigned                stride_uv;
        fixed20_12              x;
index c996a38..50b761c 100644 (file)
@@ -1030,20 +1030,17 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
                tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
                tegra_dc_writel(dc,
-                               (unsigned long)win->phys_addr +
-                               (unsigned long)win->offset,
+                               (unsigned long)win->phys_addr,
                                DC_WINBUF_START_ADDR);
 
                if (!yuvp) {
                        tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE);
                } else {
                        tegra_dc_writel(dc,
-                                       (unsigned long)win->phys_addr +
-                                       (unsigned long)win->offset_u,
+                                       (unsigned long)win->phys_addr_u,
                                        DC_WINBUF_START_ADDR_U);
                        tegra_dc_writel(dc,
-                                       (unsigned long)win->phys_addr +
-                                       (unsigned long)win->offset_v,
+                                       (unsigned long)win->phys_addr_v,
                                        DC_WINBUF_START_ADDR_V);
                        tegra_dc_writel(dc,
                                        LINE_STRIDE(win->stride) |
index 6e11107..01454f5 100644 (file)
@@ -44,9 +44,10 @@ static int head_count;
 
 struct tegra_dc_ext_flip_win {
        struct tegra_dc_ext_flip_windowattr     attr;
-       struct nvmap_handle_ref                 *handle;
-       /* ugh. is this really necessary */
+       struct nvmap_handle_ref                 *handle[TEGRA_DC_NUM_PLANES];
        dma_addr_t                              phys_addr;
+       dma_addr_t                              phys_addr_u;
+       dma_addr_t                              phys_addr_v;
        u32                                     syncpt_max;
 };
 
@@ -177,9 +178,9 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
 {
        struct tegra_dc_ext_win *ext_win = &ext->win[win->idx];
 
-       if (flip_win->handle == NULL) {
+       if (flip_win->handle[TEGRA_DC_Y] == NULL) {
                win->flags = 0;
-               ext_win->cur_handle = NULL;
+               memset(ext_win->cur_handle, 0, sizeof(ext_win->cur_handle));
                return 0;
        }
 
@@ -199,12 +200,20 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
        win->out_w = flip_win->attr.out_w;
        win->out_h = flip_win->attr.out_h;
        win->z = flip_win->attr.z;
-       ext_win->cur_handle = flip_win->handle;
+       memcpy(ext_win->cur_handle, flip_win->handle,
+              sizeof(ext_win->cur_handle));
 
        /* XXX verify that this won't read outside of the surface */
        win->phys_addr = flip_win->phys_addr + flip_win->attr.offset;
-       win->offset_u = flip_win->attr.offset_u + flip_win->attr.offset;
-       win->offset_v = flip_win->attr.offset_v + flip_win->attr.offset;
+
+       win->phys_addr_u = flip_win->handle[TEGRA_DC_U] ?
+               flip_win->phys_addr_u : flip_win->phys_addr;
+       win->phys_addr_u += flip_win->attr.offset_u;
+
+       win->phys_addr_v = flip_win->handle[TEGRA_DC_V] ?
+               flip_win->phys_addr_v : flip_win->phys_addr;
+       win->phys_addr_v += flip_win->attr.offset_v;
+
        win->stride = flip_win->attr.stride;
        win->stride_uv = flip_win->attr.stride_uv;
 
@@ -225,7 +234,8 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work)
                container_of(work, struct tegra_dc_ext_flip_data, work);
        struct tegra_dc_ext *ext = data->ext;
        struct tegra_dc_win *wins[DC_N_WINDOWS];
-       struct nvmap_handle_ref *unpin_handles[DC_N_WINDOWS];
+       struct nvmap_handle_ref *unpin_handles[DC_N_WINDOWS *
+                                              TEGRA_DC_NUM_PLANES];
        int i, nr_unpin = 0, nr_win = 0;
 
        for (i = 0; i < DC_N_WINDOWS; i++) {
@@ -240,9 +250,16 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work)
                win = tegra_dc_get_window(ext->dc, index);
                ext_win = &ext->win[index];
 
-               if ((win->flags & TEGRA_WIN_FLAG_ENABLED) &&
-                   ext_win->cur_handle)
-                       unpin_handles[nr_unpin++] = ext_win->cur_handle;
+               if (win->flags & TEGRA_WIN_FLAG_ENABLED) {
+                       int j;
+                       for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) {
+                               if (!ext_win->cur_handle[j])
+                                       continue;
+
+                               unpin_handles[nr_unpin++] =
+                                       ext_win->cur_handle[j];
+                       }
+               }
 
                tegra_dc_ext_set_windowattr(ext, win, &data->win[i]);
 
@@ -383,10 +400,34 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
                        continue;
 
                ret = tegra_dc_ext_pin_window(user, flip_win->attr.buff_id,
-                                             &flip_win->handle,
+                                             &flip_win->handle[TEGRA_DC_Y],
                                              &flip_win->phys_addr);
                if (ret)
                        goto fail_pin;
+
+               if (flip_win->attr.buff_id_u) {
+                       ret = tegra_dc_ext_pin_window(user,
+                                             flip_win->attr.buff_id_u,
+                                             &flip_win->handle[TEGRA_DC_U],
+                                             &flip_win->phys_addr_u);
+                       if (ret)
+                               goto fail_pin;
+               } else {
+                       flip_win->handle[TEGRA_DC_U] = NULL;
+                       flip_win->phys_addr_u = 0;
+               }
+
+               if (flip_win->attr.buff_id_v) {
+                       ret = tegra_dc_ext_pin_window(user,
+                                             flip_win->attr.buff_id_v,
+                                             &flip_win->handle[TEGRA_DC_V],
+                                             &flip_win->phys_addr_v);
+                       if (ret)
+                               goto fail_pin;
+               } else {
+                       flip_win->handle[TEGRA_DC_V] = NULL;
+                       flip_win->phys_addr_v = 0;
+               }
        }
 
        ret = lock_windows_for_flip(user, args);
@@ -427,12 +468,15 @@ unlock:
        unlock_windows_for_flip(user, args);
 
 fail_pin:
-       while (i--) {
-               if (!data->win[i].handle)
-                       continue;
+       for (i = 0; i < DC_N_WINDOWS; i++) {
+               int j;
+               for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) {
+                       if (!data->win[i].handle[j])
+                               continue;
 
-               nvmap_unpin(ext->nvmap, data->win[i].handle);
-               nvmap_free(ext->nvmap, data->win[i].handle);
+                       nvmap_unpin(ext->nvmap, data->win[i].handle[j]);
+                       nvmap_free(ext->nvmap, data->win[i].handle[j]);
+               }
        }
        kfree(data);
 
index 6f0d0e4..54a10b2 100644 (file)
@@ -36,6 +36,13 @@ struct tegra_dc_ext_user {
        struct nvmap_client     *nvmap;
 };
 
+enum {
+       TEGRA_DC_Y,
+       TEGRA_DC_U,
+       TEGRA_DC_V,
+       TEGRA_DC_NUM_PLANES,
+};
+
 struct tegra_dc_ext_win {
        struct tegra_dc_ext     *ext;
 
@@ -45,7 +52,8 @@ struct tegra_dc_ext_win {
 
        struct mutex            lock;
 
-       struct nvmap_handle_ref *cur_handle;
+       /* Current nvmap handle (if any) for Y, U, V planes */
+       struct nvmap_handle_ref *cur_handle[TEGRA_DC_NUM_PLANES];
 
        struct workqueue_struct *flip_wq;
 };
index 623c26a..057241e 100644 (file)
@@ -213,8 +213,8 @@ static int tegra_overlay_set_windowattr(struct tegra_overlay_info *overlay,
 
        /* STOPSHIP verify that this won't read outside of the surface */
        win->phys_addr = flip_win->phys_addr + flip_win->attr.offset;
-       win->offset_u = flip_win->attr.offset_u + flip_win->attr.offset;
-       win->offset_v = flip_win->attr.offset_v + flip_win->attr.offset;
+       win->phys_addr_u = flip_win->phys_addr + flip_win->attr.offset_u;
+       win->phys_addr_v = flip_win->phys_addr + flip_win->attr.offset_v;
        win->stride = flip_win->attr.stride;
        win->stride_uv = flip_win->attr.stride_uv;
 
index b7acecc..b170bcd 100644 (file)
@@ -111,8 +111,8 @@ static int tegra_fb_set_par(struct fb_info *info)
                tegra_fb->win->stride = round_up(info->fix.line_length,
                                                TEGRA_LINEAR_PITCH_ALIGNMENT);
                tegra_fb->win->stride_uv = 0;
-               tegra_fb->win->offset_u = 0;
-               tegra_fb->win->offset_v = 0;
+               tegra_fb->win->phys_addr_u = 0;
+               tegra_fb->win->phys_addr_v = 0;
        }
 
        if (var->pixclock) {
@@ -436,8 +436,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        win->z = 0;
        win->phys_addr = fb_phys;
        win->virt_addr = fb_base;
-       win->offset_u = 0;
-       win->offset_v = 0;
+       win->phys_addr_u = 0;
+       win->phys_addr_v = 0;
        win->stride = fb_data->xres * fb_data->bits_per_pixel / 8;
        /* Pad the stride to 16-byte boundary. */
        win->stride = round_up(win->stride, TEGRA_LINEAR_PITCH_ALIGNMENT);
index 1181848..824afea 100644 (file)
@@ -82,8 +82,11 @@ struct tegra_dc_ext_flip_windowattr {
        struct timespec timestamp;
        __u32   pre_syncpt_id;
        __u32   pre_syncpt_val;
+       /* These are optional; if zero, U and V are taken from buff_id */
+       __u32   buff_id_u;
+       __u32   buff_id_v;
        /* Leave some wiggle room for future expansion */
-       __u32   pad[8];
+       __u32   pad[6];
 };
 
 #define TEGRA_DC_EXT_FLIP_N_WINDOWS    3