diff mbox

[1/2] i-g-t: Prep work for adding NV12 testcase

Message ID 1430536299-17547-2-git-send-email-chandra.konduru@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Chandra Konduru May 2, 2015, 3:11 a.m. UTC
From: chandra konduru <chandra.konduru@intel.com>

This patch adds necessary prep work for nv12 testcase:
  - updated fb allocation functions to handle NV12 format
  - igt helper function to return png image size
  - igt helper function to calculate start of uv in a given NV12 buffer
  - igt helper function to map buffer for host access
  - populates fb->...[4] parameters for NV12
  - igt helper function to convert RGB data to NV12
  - updated drm_format to bpp to handle NV12
  - updated fast copy blit function to deal NV12 subplanes
  - made an update to kms_render testcase due to above changes

Signed-off-by: chandra konduru <chandra.konduru@intel.com>
---
 lib/igt_fb.c            | 316 +++++++++++++++++++++++++++++++++++++++++++++---
 lib/igt_fb.h            |   9 +-
 lib/intel_batchbuffer.c |  16 ++-
 lib/intel_batchbuffer.h |   3 +-
 lib/intel_reg.h         |   1 +
 lib/ioctl_wrappers.c    |  10 +-
 lib/ioctl_wrappers.h    |   2 +-
 tests/kms_render.c      |   4 +-
 8 files changed, 334 insertions(+), 27 deletions(-)

Comments

Thomas Wood May 12, 2015, 3:47 p.m. UTC | #1
On 2 May 2015 at 04:11, Chandra Konduru <chandra.konduru@intel.com> wrote:
> From: chandra konduru <chandra.konduru@intel.com>
>
> This patch adds necessary prep work for nv12 testcase:
>   - updated fb allocation functions to handle NV12 format
>   - igt helper function to return png image size
>   - igt helper function to calculate start of uv in a given NV12 buffer
>   - igt helper function to map buffer for host access
>   - populates fb->...[4] parameters for NV12
>   - igt helper function to convert RGB data to NV12
>   - updated drm_format to bpp to handle NV12
>   - updated fast copy blit function to deal NV12 subplanes
>   - made an update to kms_render testcase due to above changes

Could you split these changes into seperate patches? The
igt_get_image_size function would be especially useful separately as
you have suggest that it should be used to fix kms_plane_scaling image
loading issue.


>
> Signed-off-by: chandra konduru <chandra.konduru@intel.com>
> ---
>  lib/igt_fb.c            | 316 +++++++++++++++++++++++++++++++++++++++++++++---
>  lib/igt_fb.h            |   9 +-
>  lib/intel_batchbuffer.c |  16 ++-
>  lib/intel_batchbuffer.h |   3 +-
>  lib/intel_reg.h         |   1 +
>  lib/ioctl_wrappers.c    |  10 +-
>  lib/ioctl_wrappers.h    |   2 +-
>  tests/kms_render.c      |   4 +-
>  8 files changed, 334 insertions(+), 27 deletions(-)
>
> diff --git a/lib/igt_fb.c b/lib/igt_fb.c
> index cc4b8ee..dedee9e 100644
> --- a/lib/igt_fb.c
> +++ b/lib/igt_fb.c
> @@ -74,7 +74,7 @@ static struct format_desc_struct {
>
>
>  /* helpers to create nice-looking framebuffers */
> -static int create_bo_for_fb(int fd, int width, int height, int bpp,
> +static int create_bo_for_fb(int fd, int width, int height, int bpp, int bpp2,
>                             uint64_t tiling, unsigned bo_size,
>                             uint32_t *gem_handle_ret,
>                             unsigned *size_ret,
> @@ -99,13 +99,17 @@ static int create_bo_for_fb(int fd, int width, int height, int bpp,
>                 for (stride = 512; stride < v; stride *= 2)
>                         ;
>
> -               v = stride * height;
> +               /* planar formats height is 1.5x */
> +               v = stride * (bpp2 ? (height * 3) / 2 : height);
> +
>                 for (size = 1024*1024; size < v; size *= 2)
>                         ;
>         } else {
>                 /* Scan-out has a 64 byte alignment restriction */
>                 stride = (width * (bpp / 8) + 63) & ~63;
> -               size = stride * height;
> +
> +               /* planar formats height is 1.5x */
> +               size = stride * (bpp2 ? (height * 3) / 2 : height);
>         }
>
>         if (bo_size == 0)
> @@ -393,6 +397,75 @@ void igt_paint_image(cairo_t *cr, const char *filename,
>  }
>
>  /**
> + * igt_get_image_size:
> + * @filename: filename of the png image
> + * @width: width of the image
> + * @height: height of the image
> + *
> + * This function returns @width and @height of the png image in @filename,
> + * which is loaded from the package data directory.
> + */
> +void
> +igt_get_image_size(const char *filename, int *width, int *height)
> +{
> +       cairo_surface_t *image;
> +       FILE* f;
> +
> +       f = igt_fopen_data(filename);
> +
> +       image = cairo_image_surface_create_from_png_stream(&stdio_read_func, f);
> +       igt_assert(cairo_surface_status(image) == CAIRO_STATUS_SUCCESS);
> +
> +       *width = cairo_image_surface_get_width(image);
> +       *height = cairo_image_surface_get_height(image);
> +
> +       cairo_surface_destroy(image);
> +
> +       fclose(f);
> +}
> +
> +
> +/**
> + * igt_fb_calc_uv:
> + * @fb: pointer to an #igt_fb structure
> + *
> + * This function calculates UV offset in bytes and UV starting line number
> + * for requested NV12 @fb.
> + */
> +void
> +igt_fb_calc_uv(struct igt_fb *fb)
> +{
> +       if (fb->drm_format != DRM_FORMAT_NV12)
> +               return;
> +
> +       switch (fb->tiling) {
> +       case LOCAL_DRM_FORMAT_MOD_NONE:
> +               fb->uv_y_start = fb->height;
> +               break;
> +       case LOCAL_I915_FORMAT_MOD_X_TILED:
> +               fb->uv_y_start = fb->height;
> +               break;
> +       case LOCAL_I915_FORMAT_MOD_Y_TILED:
> +               fb->uv_y_start = fb->height;
> +               break;
> +       case LOCAL_I915_FORMAT_MOD_Yf_TILED:
> +               /* tile-Yf requires uv to start on a new tile row */
> +               if (fb->height % 64)
> +                       fb->uv_y_start = (fb->height + 63) & ~63;
> +               else
> +                       fb->uv_y_start = fb->height;
> +               break;
> +       default:
> +               igt_assert(0);
> +       }
> +
> +       fb->uv_offset = fb->uv_y_start * fb->stride;
> +
> +       /* assert that fb has enough lines to hold y and uv sub-planes */
> +       igt_assert(fb->size / fb->stride >= fb->uv_y_start + fb->height / 2);
> +}
> +
> +/**
>   * igt_create_fb_with_bo_size:
>   * @fd: open i915 drm file descriptor
>   * @width: width of the framebuffer in pixel
> @@ -418,24 +491,32 @@ igt_create_fb_with_bo_size(int fd, int width, int height,
>                            struct igt_fb *fb, unsigned bo_size)
>  {
>         uint32_t fb_id;
> -       int bpp;
> +       int bpp, bpp2;
>
>         memset(fb, 0, sizeof(*fb));
>
> -       bpp = igt_drm_format_to_bpp(format);
> +       bpp = igt_drm_format_to_bpp(format, 0);
> +       bpp2 = igt_drm_format_to_bpp(format, 1);
>
> -       igt_debug("%s(width=%d, height=%d, format=0x%x [bpp=%d], tiling=0x%"PRIx64", size=%d\n",
> -                 __func__, width, height, format, bpp, tiling, bo_size);
> -       do_or_die(create_bo_for_fb(fd, width, height, bpp, tiling, bo_size,
> +       igt_debug("%s(width=%d, height=%d, format=0x%x [bpp=%d, %d], tiling=0x%"PRIx64", size=%d\n",
> +                 __func__, width, height, format, bpp, bpp2, tiling, bo_size);
> +       do_or_die(create_bo_for_fb(fd, width, height, bpp, bpp2, tiling, bo_size,
>                                    &fb->gem_handle, &fb->size, &fb->stride));
>
>         igt_debug("%s(handle=%d, pitch=%d)\n",
>                   __func__, fb->gem_handle, fb->stride);
>
> +       fb->width = width;
> +       fb->height = height;
> +       fb->tiling = tiling;
> +       fb->drm_format = format;
> +
> +       igt_fb_calc_uv(fb);
> +
>         if (tiling != LOCAL_DRM_FORMAT_MOD_NONE &&
>             tiling != LOCAL_I915_FORMAT_MOD_X_TILED) {
>                 do_or_die(__kms_addfb(fd, fb->gem_handle, width, height,
> -                                     fb->stride, format, tiling,
> +                                     fb->stride, format, tiling, fb->uv_offset,
>                                       LOCAL_DRM_MODE_FB_MODIFIERS, &fb_id));
>         } else {
>                 uint32_t handles[4];
> @@ -449,21 +530,213 @@ igt_create_fb_with_bo_size(int fd, int width, int height,
>                 handles[0] = fb->gem_handle;
>                 pitches[0] = fb->stride;
>
> +               if (format == DRM_FORMAT_NV12) {
> +                       handles[1] = fb->gem_handle;
> +                       pitches[1] = fb->stride;
> +                       offsets[1] = fb->uv_offset;
> +               }
> +
>                 do_or_die(drmModeAddFB2(fd, width, height, format,
>                                         handles, pitches, offsets,
>                                         &fb_id, 0));
>         }
>
> -       fb->width = width;
> -       fb->height = height;
> -       fb->tiling = tiling;
> -       fb->drm_format = format;
>         fb->fb_id = fb_id;
>
>         return fb_id;
>  }
>
>  /**
> + * igt_fb_gem_mmap:
> + * @fd: open i915 drm file descriptor
> + * @fb: pointer to an #igt_fb structure
> + *
> + * This function memory maps gem buffer for cpu access and saves mmap pointer
> + * in @fb.
> + */
> +void
> +igt_fb_gem_mmap(int fd, struct igt_fb *fb)
> +{
> +       if (!fb->mmap_gtt)
> +               fb->mmap_gtt = gem_mmap__gtt(fd, fb->gem_handle, fb->size,
> +                       PROT_READ | PROT_WRITE);
> +       igt_assert(fb->mmap_gtt);
> +}
> +
> +/**
> + * igt_fb_csc_xrgb_to_nv12:
> + * @fd: open i915 drm file descriptor
> + * @width: width of the framebuffer to be converted
> + * @height: height of the framebuffer to be converted
> + * @dst: pointer to an #igt_fb structure holding destination framebuffer
> + * @src: pointer to an #igt_fb structure holding source framebuffer
> + *
> + * This function converts source framebuffer into destination framebuffer
> + * format. It also mmaps the underlying gem buffer if it isn't mmaped
> + * before.
> + *
> + * For now:
> + *    - it expects both src and dst fbs are same size and tiling
> + */
> +void
> +igt_fb_csc_xrgb_to_nv12(int fd, struct igt_fb *dst_fb, struct igt_fb *src_fb)
> +{
> +       unsigned char *nv12;
> +       unsigned char *xrgb;
> +       unsigned char y, v, u;
> +       float yf, vf, uf;
> +       unsigned char r, g, b;
> +       int i, j;
> +       int xrgb_pos;
> +       int y_pos;
> +       int uv_pos;
> +       int uv_base;
> +       unsigned int obj_tiling;
> +       struct igt_fb *temp_src_fb, *temp_dst_fb;
> +       struct igt_fb fb1, fb2;
> +       int temp_src_id, temp_dst_id;
> +
> +       igt_assert(dst_fb);
> +       igt_assert(src_fb);
> +       igt_assert(dst_fb->width == src_fb->width);
> +       igt_assert(dst_fb->height == src_fb->height);
> +       igt_assert(src_fb->tiling == dst_fb->tiling);
> +
> +       igt_assert(src_fb->drm_format == DRM_FORMAT_XRGB8888);
> +       igt_assert(dst_fb->drm_format == DRM_FORMAT_NV12);
> +
> +       /*
> +        * Steps: Not the best approach but gets job done for igt
> +        *     (1) create linear temp_src, temp_dst
> +        *     (2) fast blit src --> temp src
> +        *     (3) gem map temp_src, temp_dst
> +        *     (4) host conversion & copy from temp src --> temp dst
> +        *     (5) fast blit temp dst --> dst
> +        *     (6) free temp fbs
> +        *
> +        * For Linear/X:
> +        *     perform 3 & 4 only
> +        * For Y/Yf:
> +        *     perform all steps
> +        */
> +
> +       if (src_fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED ||
> +               src_fb->tiling == LOCAL_I915_FORMAT_MOD_Yf_TILED) {
> +
> +               temp_src_fb = &fb1;
> +               temp_dst_fb = &fb2;
> +
> +               /* (1) create linear temp_src, temp_dst */
> +               temp_src_id = igt_create_fb(fd,
> +                       src_fb->width, src_fb->height,
> +                       src_fb->drm_format,
> +                       LOCAL_DRM_FORMAT_MOD_NONE,
> +                       temp_src_fb);
> +               igt_assert(temp_src_id);
> +
> +               temp_dst_id = igt_create_fb(fd,
> +                       dst_fb->width, dst_fb->height,
> +                       dst_fb->drm_format,
> +                       LOCAL_DRM_FORMAT_MOD_NONE,
> +                       temp_dst_fb);
> +               igt_assert(temp_dst_id);
> +
> +               if (src_fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED)
> +                       obj_tiling = I915_TILING_Y;
> +               else
> +                       obj_tiling = I915_TILING_Yf;
> +
> +               /* (2) fast blit src --> temp src */
> +               igt_blitter_fast_copy__raw(fd,
> +                       src_fb->gem_handle,
> +                       src_fb->stride,
> +                       obj_tiling,
> +                       0, 0,
> +                       temp_src_fb->width, temp_src_fb->height,
> +                       igt_drm_format_to_bpp(temp_src_fb->drm_format, 0),
> +                       temp_src_fb->gem_handle,
> +                       temp_src_fb->stride,
> +                       I915_TILING_NONE,
> +                       0, 0);
> +       } else {
> +               temp_src_fb = src_fb;
> +               temp_dst_fb = dst_fb;
> +       }
> +
> +       /* (3) gem map temp_src, temp_dst */
> +       igt_fb_gem_mmap(fd, temp_src_fb);
> +       igt_fb_gem_mmap(fd, temp_dst_fb);
> +
> +       /* (4) host conversion & copy from temp src --> temp dst */
> +       xrgb = (unsigned char *) temp_src_fb->mmap_gtt;
> +       nv12 = (unsigned char *) temp_dst_fb->mmap_gtt;
> +       uv_base = temp_dst_fb->stride * temp_dst_fb->height;
> +
> +       for (i = 0; i < temp_src_fb->height; i++) {
> +               xrgb_pos = i * temp_src_fb->stride;
> +               y_pos = i * temp_dst_fb->stride;
> +               uv_pos = temp_dst_fb->stride * i / 2;
> +               for (j = 0; j < temp_src_fb->width; j++) {
> +                       b = xrgb[xrgb_pos++];
> +                       g = xrgb[xrgb_pos++];
> +                       r = xrgb[xrgb_pos++];
> +                       xrgb_pos++;
> +
> +                       /* use floats for intermediate calcs, to get better results */
> +                       yf =  (0.257 * r) + (0.504 * g) + (0.098 * b) + 16;
> +                       uf = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 128;
> +                       vf =  (0.439 * r) - (0.368 * g) - (0.071 * b) + 128;
> +
> +                       y = (unsigned char) yf;
> +                       u = (unsigned char) uf;
> +                       v = (unsigned char) vf;
> +
> +                       nv12[y_pos++] = y;
> +
> +                       if (!(j % 2) && !(i % 2)) {
> +                               nv12[uv_base + uv_pos++] = u;
> +                               nv12[uv_base + uv_pos++] = v;
> +                       }
> +               }
> +       }
> +
> +       /* (5) fast blit temp dst --> dst */
> +       if (dst_fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED ||
> +               dst_fb->tiling == LOCAL_I915_FORMAT_MOD_Yf_TILED) {
> +
> +               /* blit y-plane */
> +               igt_blitter_fast_copy__raw(fd,
> +                       temp_dst_fb->gem_handle,
> +                       temp_dst_fb->stride,
> +                       I915_TILING_NONE,
> +                       0, 0,
> +                       dst_fb->width, dst_fb->height,
> +                       igt_drm_format_to_bpp(dst_fb->drm_format, 0),
> +                       dst_fb->gem_handle,
> +                       dst_fb->stride,
> +                       obj_tiling,
> +                       0, 0);
> +
> +               /* blit uv-plane */
> +               igt_blitter_fast_copy__raw(fd,
> +                       temp_dst_fb->gem_handle,
> +                       temp_dst_fb->stride,
> +                       I915_TILING_NONE,
> +                       0, temp_dst_fb->uv_y_start,
> +                       dst_fb->width, dst_fb->height,
> +                       igt_drm_format_to_bpp(dst_fb->drm_format, 1),
> +                       dst_fb->gem_handle,
> +                       dst_fb->stride,
> +                       obj_tiling,
> +                       0, dst_fb->uv_y_start);
> +
> +               /* (6) free temp fbs */
> +               igt_remove_fb(fd, temp_src_fb);
> +               igt_remove_fb(fd, temp_dst_fb);
> +       }
> +}
> +
> +/**
>   * igt_create_fb:
>   * @fd: open i915 drm file descriptor
>   * @width: width of the framebuffer in pixel
> @@ -686,6 +959,7 @@ static void destroy_cairo_surface__blit(void *arg)
>                                    I915_TILING_NONE,
>                                    0, 0, /* src_x, src_y */
>                                    fb->width, fb->height,
> +                                  0,
>                                    fb->gem_handle,
>                                    fb->stride,
>                                    obj_tiling,
> @@ -712,8 +986,8 @@ static void create_cairo_surface__blit(int fd, struct igt_fb *fb)
>          * cairo). This linear bo will be then blitted to its final
>          * destination, tiling it at the same time.
>          */
> -       bpp = igt_drm_format_to_bpp(fb->drm_format);
> -       ret = create_bo_for_fb(fd, fb->width, fb->height, bpp,
> +       bpp = igt_drm_format_to_bpp(fb->drm_format, 0);
> +       ret = create_bo_for_fb(fd, fb->width, fb->height, bpp, 0,
>                                 LOCAL_DRM_FORMAT_MOD_NONE, 0,
>                                 &blit->linear.handle,
>                                 &blit->linear.size,
> @@ -734,6 +1008,7 @@ static void create_cairo_surface__blit(int fd, struct igt_fb *fb)
>                                    obj_tiling,
>                                    0, 0, /* src_x, src_y */
>                                    fb->width, fb->height,
> +                                  bpp,
>                                    blit->linear.handle,
>                                    blit->linear.stride,
>                                    I915_TILING_NONE,
> @@ -893,15 +1168,24 @@ uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth)
>  /**
>   * igt_drm_format_to_bpp:
>   * @drm_format: drm fourcc pixel format code
> + * @plane: plane id
>   *
>   * Returns:
>   * The bits per pixel for the given drm fourcc pixel format code. Fails hard if
>   * no match was found.
>   */
> -uint32_t igt_drm_format_to_bpp(uint32_t drm_format)
> +uint32_t igt_drm_format_to_bpp(uint32_t drm_format, int plane)
>  {
>         struct format_desc_struct *f;
>
> +       if (drm_format == DRM_FORMAT_NV12 && plane == 0)
> +               return 8;
> +       if (drm_format == DRM_FORMAT_NV12 && plane == 1)
> +               return 16;
> +
> +       if (plane)
> +               return 0;
> +
>         for_each_format(f)
>                 if (f->drm_id == drm_format)
>                         return f->bpp;
> diff --git a/lib/igt_fb.h b/lib/igt_fb.h
> index a07acd2..9e227e8 100644
> --- a/lib/igt_fb.h
> +++ b/lib/igt_fb.h
> @@ -54,10 +54,13 @@ struct igt_fb {
>         uint64_t tiling;
>         unsigned size;
>         cairo_surface_t *cairo_surface;
> +       void *mmap_gtt;
>         uint32_t src_x;
>         uint32_t src_y;
>         uint32_t src_w;
>         uint32_t src_h;
> +       uint32_t uv_y_start;
> +       uint32_t uv_offset;
>  };
>
>  enum igt_text_align {
> @@ -82,6 +85,9 @@ unsigned int igt_create_color_fb(int fd, int width, int height,
>  unsigned int igt_create_stereo_fb(int drm_fd, drmModeModeInfo *mode,
>                                   uint32_t format, uint64_t tiling);
>  void igt_remove_fb(int fd, struct igt_fb *fb);
> +void igt_fb_gem_mmap(int fd, struct igt_fb *fb);
> +void igt_fb_csc_xrgb_to_nv12(int fd, struct igt_fb *dst, struct igt_fb *src);
> +void igt_fb_calc_uv(struct igt_fb *fb);
>
>  /* cairo-based painting */
>  cairo_t *igt_get_cairo_ctx(int fd, struct igt_fb *fb);
> @@ -98,10 +104,11 @@ void igt_write_fb_to_png(int fd, struct igt_fb *fb, const char *filename);
>  int igt_cairo_printf_line(cairo_t *cr, enum igt_text_align align,
>                                double yspacing, const char *fmt, ...)
>                                __attribute__((format (printf, 4, 5)));
> +void igt_get_image_size(const char *filename, int *width, int *height);
>
>  /* helpers to handle drm fourcc codes */
>  uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth);
> -uint32_t igt_drm_format_to_bpp(uint32_t drm_format);
> +uint32_t igt_drm_format_to_bpp(uint32_t drm_format, int plane);
>  const char *igt_format_str(uint32_t drm_format);
>  void igt_get_all_formats(const uint32_t **formats, int *format_count);
>
> diff --git a/lib/intel_batchbuffer.c b/lib/intel_batchbuffer.c
> index 8b68e52..a2cd6ab 100644
> --- a/lib/intel_batchbuffer.c
> +++ b/lib/intel_batchbuffer.c
> @@ -518,7 +518,7 @@ static uint32_t fast_copy_dword0(unsigned int src_tiling,
>  }
>
>  static uint32_t fast_copy_dword1(unsigned int src_tiling,
> -                                unsigned int dst_tiling)
> +                                unsigned int dst_tiling, uint32_t color_depth)
>  {
>         uint32_t dword1 = 0;
>
> @@ -527,7 +527,10 @@ static uint32_t fast_copy_dword1(unsigned int src_tiling,
>         if (dst_tiling == I915_TILING_Yf)
>                 dword1 |= XY_FAST_COPY_DST_TILING_Yf;
>
> -       dword1 |= XY_FAST_COPY_COLOR_DEPTH_32;
> +       if (color_depth == 8)
> +               dword1 |= XY_FAST_COPY_COLOR_DEPTH_8;
> +       else
> +               dword1 |= XY_FAST_COPY_COLOR_DEPTH_32;
>
>         return dword1;
>  }
> @@ -585,6 +588,7 @@ static void exec_blit(int fd,
>   * @src_y: Y coordinate of the source region to copy
>   * @width: Width of the region to copy
>   * @height: Height of the region to copy
> + * @color_depth: Color depth of the buffer
>   * @dst_handle: GEM handle of the source buffer
>   * @dst_stride: Stride (in bytes) of the destination buffer
>   * @dst_tiling: Tiling mode of the destination buffer
> @@ -602,6 +606,7 @@ void igt_blitter_fast_copy__raw(int fd,
>
>                                 /* size */
>                                 unsigned int width, unsigned int height,
> +                               uint32_t color_depth,
>
>                                 /* dst */
>                                 uint32_t dst_handle,
> @@ -620,7 +625,7 @@ void igt_blitter_fast_copy__raw(int fd,
>         src_pitch = fast_copy_pitch(src_stride, src_tiling);
>         dst_pitch = fast_copy_pitch(dst_stride, dst_tiling);
>         dword0 = fast_copy_dword0(src_tiling, dst_tiling);
> -       dword1 = fast_copy_dword1(src_tiling, dst_tiling);
> +       dword1 = fast_copy_dword1(src_tiling, dst_tiling, color_depth);
>
>  #define CHECK_RANGE(x) ((x) >= 0 && (x) < (1 << 15))
>         assert(CHECK_RANGE(src_x) && CHECK_RANGE(src_y) &&
> @@ -670,6 +675,7 @@ void igt_blitter_fast_copy__raw(int fd,
>   * @src_y: source pixel y-coordination
>   * @width: width of the copied rectangle
>   * @height: height of the copied rectangle
> + * @color_depth: Color depth of the buffer
>   * @dst: destination i-g-t buffer object
>   * @dst_x: destination pixel x-coordination
>   * @dst_y: destination pixel y-coordination
> @@ -680,7 +686,7 @@ void igt_blitter_fast_copy__raw(int fd,
>   */
>  void igt_blitter_fast_copy(struct intel_batchbuffer *batch,
>                            struct igt_buf *src, unsigned src_x, unsigned src_y,
> -                          unsigned width, unsigned height,
> +                          unsigned width, unsigned height, uint32_t color_depth,
>                            struct igt_buf *dst, unsigned dst_x, unsigned dst_y)
>  {
>         uint32_t src_pitch, dst_pitch;
> @@ -689,7 +695,7 @@ void igt_blitter_fast_copy(struct intel_batchbuffer *batch,
>         src_pitch = fast_copy_pitch(src->stride, src->tiling);
>         dst_pitch = fast_copy_pitch(dst->stride, src->tiling);
>         dword0 = fast_copy_dword0(src->tiling, dst->tiling);
> -       dword1 = fast_copy_dword1(src->tiling, dst->tiling);
> +       dword1 = fast_copy_dword1(src->tiling, dst->tiling, color_depth);
>
>  #define CHECK_RANGE(x) ((x) >= 0 && (x) < (1 << 15))
>         assert(CHECK_RANGE(src_x) && CHECK_RANGE(src_y) &&
> diff --git a/lib/intel_batchbuffer.h b/lib/intel_batchbuffer.h
> index 62c8396..0e0feed 100644
> --- a/lib/intel_batchbuffer.h
> +++ b/lib/intel_batchbuffer.h
> @@ -226,7 +226,7 @@ unsigned igt_buf_height(struct igt_buf *buf);
>
>  void igt_blitter_fast_copy(struct intel_batchbuffer *batch,
>                           struct igt_buf *src, unsigned src_x, unsigned src_y,
> -                         unsigned width, unsigned height,
> +                         unsigned width, unsigned height, uint32_t color_depth,
>                           struct igt_buf *dst, unsigned dst_x, unsigned dst_y);
>
>  void igt_blitter_fast_copy__raw(int fd,
> @@ -238,6 +238,7 @@ void igt_blitter_fast_copy__raw(int fd,
>
>                                 /* size */
>                                 unsigned int width, unsigned int height,
> +                               uint32_t color_depth,
>
>                                 /* dst */
>                                 uint32_t dst_handle,
> diff --git a/lib/intel_reg.h b/lib/intel_reg.h
> index 0ffa803..9f3de7a 100644
> --- a/lib/intel_reg.h
> +++ b/lib/intel_reg.h
> @@ -2530,6 +2530,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>  /* dword 1 */
>  #define   XY_FAST_COPY_SRC_TILING_Yf                   (1 <<  31)
>  #define   XY_FAST_COPY_DST_TILING_Yf                   (1 <<  30)
> +#define   XY_FAST_COPY_COLOR_DEPTH_8                   (0  << 24)
>  #define   XY_FAST_COPY_COLOR_DEPTH_32                  (3  << 24)
>
>  #define MI_STORE_DWORD_IMM             ((0x20<<23)|2)
> diff --git a/lib/ioctl_wrappers.c b/lib/ioctl_wrappers.c
> index a269d0f..93717a0 100644
> --- a/lib/ioctl_wrappers.c
> +++ b/lib/ioctl_wrappers.c
> @@ -54,6 +54,7 @@
>  #include "intel_chipset.h"
>  #include "intel_io.h"
>  #include "igt_debugfs.h"
> +#include "igt_fb.h"
>  #include "config.h"
>
>  #include "ioctl_wrappers.h"
> @@ -1221,7 +1222,7 @@ void igt_require_fb_modifiers(int fd)
>
>  int __kms_addfb(int fd, uint32_t handle, uint32_t width, uint32_t height,
>                 uint32_t stride, uint32_t pixel_format, uint64_t modifier,
> -               uint32_t flags, uint32_t *buf_id)
> +               uint32_t uv_offset, uint32_t flags, uint32_t *buf_id)
>  {
>         struct local_drm_mode_fb_cmd2 f;
>         int ret;
> @@ -1238,6 +1239,13 @@ int __kms_addfb(int fd, uint32_t handle, uint32_t width, uint32_t height,
>         f.pitches[0] = stride;
>         f.modifier[0] = modifier;
>
> +       if (pixel_format == DRM_FORMAT_NV12) {
> +               f.handles[1] = handle;
> +               f.pitches[1] = stride;
> +               f.offsets[1] = uv_offset;
> +               f.modifier[1] = modifier;
> +       }
> +
>         ret = drmIoctl(fd, LOCAL_DRM_IOCTL_MODE_ADDFB2, &f);
>
>         *buf_id = f.fb_id;
> diff --git a/lib/ioctl_wrappers.h b/lib/ioctl_wrappers.h
> index bc5d4bd..3d93a5e 100644
> --- a/lib/ioctl_wrappers.h
> +++ b/lib/ioctl_wrappers.h
> @@ -175,6 +175,6 @@ void igt_require_fb_modifiers(int fd);
>   */
>  int __kms_addfb(int fd, uint32_t handle, uint32_t width, uint32_t height,
>                 uint32_t stride, uint32_t pixel_format, uint64_t modifier,
> -               uint32_t flags, uint32_t *buf_id);
> +               uint32_t uv_offset, uint32_t flags, uint32_t *buf_id);
>
>  #endif /* IOCTL_WRAPPERS_H */
> diff --git a/tests/kms_render.c b/tests/kms_render.c
> index 1759eaa..4a7aaec 100644
> --- a/tests/kms_render.c
> +++ b/tests/kms_render.c
> @@ -78,8 +78,8 @@ static void gpu_blit(struct igt_fb *dst_fb, struct igt_fb *src_fb)
>
>         igt_assert(dst_fb->drm_format == src_fb->drm_format);
>         igt_assert(src_fb->drm_format == DRM_FORMAT_RGB565 ||
> -              igt_drm_format_to_bpp(src_fb->drm_format) != 16);
> -       bpp = igt_drm_format_to_bpp(src_fb->drm_format);
> +              igt_drm_format_to_bpp(src_fb->drm_format, 0) != 16);
> +       bpp = igt_drm_format_to_bpp(src_fb->drm_format, 0);
>         dst_bo = gem_handle_to_libdrm_bo(bufmgr, drm_fd, "destination",
>                                          dst_fb->gem_handle);
>         igt_assert(dst_bo);
> --
> 1.9.1
>
diff mbox

Patch

diff --git a/lib/igt_fb.c b/lib/igt_fb.c
index cc4b8ee..dedee9e 100644
--- a/lib/igt_fb.c
+++ b/lib/igt_fb.c
@@ -74,7 +74,7 @@  static struct format_desc_struct {
 
 
 /* helpers to create nice-looking framebuffers */
-static int create_bo_for_fb(int fd, int width, int height, int bpp,
+static int create_bo_for_fb(int fd, int width, int height, int bpp, int bpp2,
 			    uint64_t tiling, unsigned bo_size,
 			    uint32_t *gem_handle_ret,
 			    unsigned *size_ret,
@@ -99,13 +99,17 @@  static int create_bo_for_fb(int fd, int width, int height, int bpp,
 		for (stride = 512; stride < v; stride *= 2)
 			;
 
-		v = stride * height;
+		/* planar formats height is 1.5x */
+		v = stride * (bpp2 ? (height * 3) / 2 : height);
+
 		for (size = 1024*1024; size < v; size *= 2)
 			;
 	} else {
 		/* Scan-out has a 64 byte alignment restriction */
 		stride = (width * (bpp / 8) + 63) & ~63;
-		size = stride * height;
+
+		/* planar formats height is 1.5x */
+		size = stride * (bpp2 ? (height * 3) / 2 : height);
 	}
 
 	if (bo_size == 0)
@@ -393,6 +397,75 @@  void igt_paint_image(cairo_t *cr, const char *filename,
 }
 
 /**
+ * igt_get_image_size:
+ * @filename: filename of the png image
+ * @width: width of the image
+ * @height: height of the image
+ *
+ * This function returns @width and @height of the png image in @filename,
+ * which is loaded from the package data directory.
+ */
+void
+igt_get_image_size(const char *filename, int *width, int *height)
+{
+	cairo_surface_t *image;
+	FILE* f;
+
+	f = igt_fopen_data(filename);
+
+	image = cairo_image_surface_create_from_png_stream(&stdio_read_func, f);
+	igt_assert(cairo_surface_status(image) == CAIRO_STATUS_SUCCESS);
+
+	*width = cairo_image_surface_get_width(image);
+	*height = cairo_image_surface_get_height(image);
+
+	cairo_surface_destroy(image);
+
+	fclose(f);
+}
+
+
+/**
+ * igt_fb_calc_uv:
+ * @fb: pointer to an #igt_fb structure
+ *
+ * This function calculates UV offset in bytes and UV starting line number
+ * for requested NV12 @fb.
+ */
+void
+igt_fb_calc_uv(struct igt_fb *fb)
+{
+	if (fb->drm_format != DRM_FORMAT_NV12)
+		return;
+
+	switch (fb->tiling) {
+	case LOCAL_DRM_FORMAT_MOD_NONE:
+		fb->uv_y_start = fb->height;
+		break;
+	case LOCAL_I915_FORMAT_MOD_X_TILED:
+		fb->uv_y_start = fb->height;
+		break;
+	case LOCAL_I915_FORMAT_MOD_Y_TILED:
+		fb->uv_y_start = fb->height;
+		break;
+	case LOCAL_I915_FORMAT_MOD_Yf_TILED:
+		/* tile-Yf requires uv to start on a new tile row */
+		if (fb->height % 64)
+			fb->uv_y_start = (fb->height + 63) & ~63;
+		else
+			fb->uv_y_start = fb->height;
+		break;
+	default:
+		igt_assert(0);
+	}
+
+	fb->uv_offset = fb->uv_y_start * fb->stride;
+
+	/* assert that fb has enough lines to hold y and uv sub-planes */
+	igt_assert(fb->size / fb->stride >= fb->uv_y_start + fb->height / 2);
+}
+
+/**
  * igt_create_fb_with_bo_size:
  * @fd: open i915 drm file descriptor
  * @width: width of the framebuffer in pixel
@@ -418,24 +491,32 @@  igt_create_fb_with_bo_size(int fd, int width, int height,
 			   struct igt_fb *fb, unsigned bo_size)
 {
 	uint32_t fb_id;
-	int bpp;
+	int bpp, bpp2;
 
 	memset(fb, 0, sizeof(*fb));
 
-	bpp = igt_drm_format_to_bpp(format);
+	bpp = igt_drm_format_to_bpp(format, 0);
+	bpp2 = igt_drm_format_to_bpp(format, 1);
 
-	igt_debug("%s(width=%d, height=%d, format=0x%x [bpp=%d], tiling=0x%"PRIx64", size=%d\n",
-		  __func__, width, height, format, bpp, tiling, bo_size);
-	do_or_die(create_bo_for_fb(fd, width, height, bpp, tiling, bo_size,
+	igt_debug("%s(width=%d, height=%d, format=0x%x [bpp=%d, %d], tiling=0x%"PRIx64", size=%d\n",
+		  __func__, width, height, format, bpp, bpp2, tiling, bo_size);
+	do_or_die(create_bo_for_fb(fd, width, height, bpp, bpp2, tiling, bo_size,
 				   &fb->gem_handle, &fb->size, &fb->stride));
 
 	igt_debug("%s(handle=%d, pitch=%d)\n",
 		  __func__, fb->gem_handle, fb->stride);
 
+	fb->width = width;
+	fb->height = height;
+	fb->tiling = tiling;
+	fb->drm_format = format;
+
+	igt_fb_calc_uv(fb);
+
 	if (tiling != LOCAL_DRM_FORMAT_MOD_NONE &&
 	    tiling != LOCAL_I915_FORMAT_MOD_X_TILED) {
 		do_or_die(__kms_addfb(fd, fb->gem_handle, width, height,
-				      fb->stride, format, tiling,
+				      fb->stride, format, tiling, fb->uv_offset,
 				      LOCAL_DRM_MODE_FB_MODIFIERS, &fb_id));
 	} else {
 		uint32_t handles[4];
@@ -449,21 +530,213 @@  igt_create_fb_with_bo_size(int fd, int width, int height,
 		handles[0] = fb->gem_handle;
 		pitches[0] = fb->stride;
 
+		if (format == DRM_FORMAT_NV12) {
+			handles[1] = fb->gem_handle;
+			pitches[1] = fb->stride;
+			offsets[1] = fb->uv_offset;
+		}
+
 		do_or_die(drmModeAddFB2(fd, width, height, format,
 					handles, pitches, offsets,
 					&fb_id, 0));
 	}
 
-	fb->width = width;
-	fb->height = height;
-	fb->tiling = tiling;
-	fb->drm_format = format;
 	fb->fb_id = fb_id;
 
 	return fb_id;
 }
 
 /**
+ * igt_fb_gem_mmap:
+ * @fd: open i915 drm file descriptor
+ * @fb: pointer to an #igt_fb structure
+ *
+ * This function memory maps gem buffer for cpu access and saves mmap pointer
+ * in @fb.
+ */
+void
+igt_fb_gem_mmap(int fd, struct igt_fb *fb)
+{
+	if (!fb->mmap_gtt)
+		fb->mmap_gtt = gem_mmap__gtt(fd, fb->gem_handle, fb->size,
+			PROT_READ | PROT_WRITE);
+	igt_assert(fb->mmap_gtt);
+}
+
+/**
+ * igt_fb_csc_xrgb_to_nv12:
+ * @fd: open i915 drm file descriptor
+ * @width: width of the framebuffer to be converted
+ * @height: height of the framebuffer to be converted
+ * @dst: pointer to an #igt_fb structure holding destination framebuffer
+ * @src: pointer to an #igt_fb structure holding source framebuffer
+ *
+ * This function converts source framebuffer into destination framebuffer
+ * format. It also mmaps the underlying gem buffer if it isn't mmaped
+ * before.
+ *
+ * For now:
+ *    - it expects both src and dst fbs are same size and tiling
+ */
+void
+igt_fb_csc_xrgb_to_nv12(int fd, struct igt_fb *dst_fb, struct igt_fb *src_fb)
+{
+	unsigned char *nv12;
+	unsigned char *xrgb;
+	unsigned char y, v, u;
+	float yf, vf, uf;
+	unsigned char r, g, b;
+	int i, j;
+	int xrgb_pos;
+	int y_pos;
+	int uv_pos;
+	int uv_base;
+	unsigned int obj_tiling;
+	struct igt_fb *temp_src_fb, *temp_dst_fb;
+	struct igt_fb fb1, fb2;
+	int temp_src_id, temp_dst_id;
+
+	igt_assert(dst_fb);
+	igt_assert(src_fb);
+	igt_assert(dst_fb->width == src_fb->width);
+	igt_assert(dst_fb->height == src_fb->height);
+	igt_assert(src_fb->tiling == dst_fb->tiling);
+
+	igt_assert(src_fb->drm_format == DRM_FORMAT_XRGB8888);
+	igt_assert(dst_fb->drm_format == DRM_FORMAT_NV12);
+
+	/*
+	 * Steps: Not the best approach but gets job done for igt
+	 *     (1) create linear temp_src, temp_dst
+	 *     (2) fast blit src --> temp src
+	 *     (3) gem map temp_src, temp_dst
+	 *     (4) host conversion & copy from temp src --> temp dst
+	 *     (5) fast blit temp dst --> dst
+	 *     (6) free temp fbs
+	 *
+	 * For Linear/X:
+	 *     perform 3 & 4 only
+	 * For Y/Yf:
+	 *     perform all steps
+	 */
+
+	if (src_fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED ||
+		src_fb->tiling == LOCAL_I915_FORMAT_MOD_Yf_TILED) {
+
+		temp_src_fb = &fb1;
+		temp_dst_fb = &fb2;
+
+		/* (1) create linear temp_src, temp_dst */
+		temp_src_id = igt_create_fb(fd,
+			src_fb->width, src_fb->height,
+			src_fb->drm_format,
+			LOCAL_DRM_FORMAT_MOD_NONE,
+			temp_src_fb);
+		igt_assert(temp_src_id);
+
+		temp_dst_id = igt_create_fb(fd,
+			dst_fb->width, dst_fb->height,
+			dst_fb->drm_format,
+			LOCAL_DRM_FORMAT_MOD_NONE,
+			temp_dst_fb);
+		igt_assert(temp_dst_id);
+
+		if (src_fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED)
+			obj_tiling = I915_TILING_Y;
+		else
+			obj_tiling = I915_TILING_Yf;
+
+		/* (2) fast blit src --> temp src */
+		igt_blitter_fast_copy__raw(fd,
+			src_fb->gem_handle,
+			src_fb->stride,
+			obj_tiling,
+			0, 0,
+			temp_src_fb->width, temp_src_fb->height,
+			igt_drm_format_to_bpp(temp_src_fb->drm_format, 0),
+			temp_src_fb->gem_handle,
+			temp_src_fb->stride,
+			I915_TILING_NONE,
+			0, 0);
+	} else {
+		temp_src_fb = src_fb;
+		temp_dst_fb = dst_fb;
+	}
+
+	/* (3) gem map temp_src, temp_dst */
+	igt_fb_gem_mmap(fd, temp_src_fb);
+	igt_fb_gem_mmap(fd, temp_dst_fb);
+
+	/* (4) host conversion & copy from temp src --> temp dst */
+	xrgb = (unsigned char *) temp_src_fb->mmap_gtt;
+	nv12 = (unsigned char *) temp_dst_fb->mmap_gtt;
+	uv_base = temp_dst_fb->stride * temp_dst_fb->height;
+
+	for (i = 0; i < temp_src_fb->height; i++) {
+		xrgb_pos = i * temp_src_fb->stride;
+		y_pos = i * temp_dst_fb->stride;
+		uv_pos = temp_dst_fb->stride * i / 2;
+		for (j = 0; j < temp_src_fb->width; j++) {
+			b = xrgb[xrgb_pos++];
+			g = xrgb[xrgb_pos++];
+			r = xrgb[xrgb_pos++];
+			xrgb_pos++;
+
+			/* use floats for intermediate calcs, to get better results */
+			yf =  (0.257 * r) + (0.504 * g) + (0.098 * b) + 16;
+			uf = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 128;
+			vf =  (0.439 * r) - (0.368 * g) - (0.071 * b) + 128;
+
+			y = (unsigned char) yf;
+			u = (unsigned char) uf;
+			v = (unsigned char) vf;
+
+			nv12[y_pos++] = y;
+
+			if (!(j % 2) && !(i % 2)) {
+				nv12[uv_base + uv_pos++] = u;
+				nv12[uv_base + uv_pos++] = v;
+			}
+		}
+	}
+
+	/* (5) fast blit temp dst --> dst */
+	if (dst_fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED ||
+		dst_fb->tiling == LOCAL_I915_FORMAT_MOD_Yf_TILED) {
+
+		/* blit y-plane */
+		igt_blitter_fast_copy__raw(fd,
+			temp_dst_fb->gem_handle,
+			temp_dst_fb->stride,
+			I915_TILING_NONE,
+			0, 0,
+			dst_fb->width, dst_fb->height,
+			igt_drm_format_to_bpp(dst_fb->drm_format, 0),
+			dst_fb->gem_handle,
+			dst_fb->stride,
+			obj_tiling,
+			0, 0);
+
+		/* blit uv-plane */
+		igt_blitter_fast_copy__raw(fd,
+			temp_dst_fb->gem_handle,
+			temp_dst_fb->stride,
+			I915_TILING_NONE,
+			0, temp_dst_fb->uv_y_start,
+			dst_fb->width, dst_fb->height,
+			igt_drm_format_to_bpp(dst_fb->drm_format, 1),
+			dst_fb->gem_handle,
+			dst_fb->stride,
+			obj_tiling,
+			0, dst_fb->uv_y_start);
+
+		/* (6) free temp fbs */
+		igt_remove_fb(fd, temp_src_fb);
+		igt_remove_fb(fd, temp_dst_fb);
+	}
+}
+
+/**
  * igt_create_fb:
  * @fd: open i915 drm file descriptor
  * @width: width of the framebuffer in pixel
@@ -686,6 +959,7 @@  static void destroy_cairo_surface__blit(void *arg)
 				   I915_TILING_NONE,
 				   0, 0, /* src_x, src_y */
 				   fb->width, fb->height,
+				   0,
 				   fb->gem_handle,
 				   fb->stride,
 				   obj_tiling,
@@ -712,8 +986,8 @@  static void create_cairo_surface__blit(int fd, struct igt_fb *fb)
 	 * cairo). This linear bo will be then blitted to its final
 	 * destination, tiling it at the same time.
 	 */
-	bpp = igt_drm_format_to_bpp(fb->drm_format);
-	ret = create_bo_for_fb(fd, fb->width, fb->height, bpp,
+	bpp = igt_drm_format_to_bpp(fb->drm_format, 0);
+	ret = create_bo_for_fb(fd, fb->width, fb->height, bpp, 0,
 				LOCAL_DRM_FORMAT_MOD_NONE, 0,
 				&blit->linear.handle,
 				&blit->linear.size,
@@ -734,6 +1008,7 @@  static void create_cairo_surface__blit(int fd, struct igt_fb *fb)
 				   obj_tiling,
 				   0, 0, /* src_x, src_y */
 				   fb->width, fb->height,
+				   bpp,
 				   blit->linear.handle,
 				   blit->linear.stride,
 				   I915_TILING_NONE,
@@ -893,15 +1168,24 @@  uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth)
 /**
  * igt_drm_format_to_bpp:
  * @drm_format: drm fourcc pixel format code
+ * @plane: plane id
  *
  * Returns:
  * The bits per pixel for the given drm fourcc pixel format code. Fails hard if
  * no match was found.
  */
-uint32_t igt_drm_format_to_bpp(uint32_t drm_format)
+uint32_t igt_drm_format_to_bpp(uint32_t drm_format, int plane)
 {
 	struct format_desc_struct *f;
 
+	if (drm_format == DRM_FORMAT_NV12 && plane == 0)
+		return 8;
+	if (drm_format == DRM_FORMAT_NV12 && plane == 1)
+		return 16;
+
+	if (plane)
+		return 0;
+
 	for_each_format(f)
 		if (f->drm_id == drm_format)
 			return f->bpp;
diff --git a/lib/igt_fb.h b/lib/igt_fb.h
index a07acd2..9e227e8 100644
--- a/lib/igt_fb.h
+++ b/lib/igt_fb.h
@@ -54,10 +54,13 @@  struct igt_fb {
 	uint64_t tiling;
 	unsigned size;
 	cairo_surface_t *cairo_surface;
+	void *mmap_gtt;
 	uint32_t src_x;
 	uint32_t src_y;
 	uint32_t src_w;
 	uint32_t src_h;
+	uint32_t uv_y_start;
+	uint32_t uv_offset;
 };
 
 enum igt_text_align {
@@ -82,6 +85,9 @@  unsigned int igt_create_color_fb(int fd, int width, int height,
 unsigned int igt_create_stereo_fb(int drm_fd, drmModeModeInfo *mode,
 				  uint32_t format, uint64_t tiling);
 void igt_remove_fb(int fd, struct igt_fb *fb);
+void igt_fb_gem_mmap(int fd, struct igt_fb *fb);
+void igt_fb_csc_xrgb_to_nv12(int fd, struct igt_fb *dst, struct igt_fb *src);
+void igt_fb_calc_uv(struct igt_fb *fb);
 
 /* cairo-based painting */
 cairo_t *igt_get_cairo_ctx(int fd, struct igt_fb *fb);
@@ -98,10 +104,11 @@  void igt_write_fb_to_png(int fd, struct igt_fb *fb, const char *filename);
 int igt_cairo_printf_line(cairo_t *cr, enum igt_text_align align,
 			       double yspacing, const char *fmt, ...)
 			       __attribute__((format (printf, 4, 5)));
+void igt_get_image_size(const char *filename, int *width, int *height);
 
 /* helpers to handle drm fourcc codes */
 uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth);
-uint32_t igt_drm_format_to_bpp(uint32_t drm_format);
+uint32_t igt_drm_format_to_bpp(uint32_t drm_format, int plane);
 const char *igt_format_str(uint32_t drm_format);
 void igt_get_all_formats(const uint32_t **formats, int *format_count);
 
diff --git a/lib/intel_batchbuffer.c b/lib/intel_batchbuffer.c
index 8b68e52..a2cd6ab 100644
--- a/lib/intel_batchbuffer.c
+++ b/lib/intel_batchbuffer.c
@@ -518,7 +518,7 @@  static uint32_t fast_copy_dword0(unsigned int src_tiling,
 }
 
 static uint32_t fast_copy_dword1(unsigned int src_tiling,
-				 unsigned int dst_tiling)
+				 unsigned int dst_tiling, uint32_t color_depth)
 {
 	uint32_t dword1 = 0;
 
@@ -527,7 +527,10 @@  static uint32_t fast_copy_dword1(unsigned int src_tiling,
 	if (dst_tiling == I915_TILING_Yf)
 		dword1 |= XY_FAST_COPY_DST_TILING_Yf;
 
-	dword1 |= XY_FAST_COPY_COLOR_DEPTH_32;
+	if (color_depth == 8)
+		dword1 |= XY_FAST_COPY_COLOR_DEPTH_8;
+	else
+		dword1 |= XY_FAST_COPY_COLOR_DEPTH_32;
 
 	return dword1;
 }
@@ -585,6 +588,7 @@  static void exec_blit(int fd,
  * @src_y: Y coordinate of the source region to copy
  * @width: Width of the region to copy
  * @height: Height of the region to copy
+ * @color_depth: Color depth of the buffer
  * @dst_handle: GEM handle of the source buffer
  * @dst_stride: Stride (in bytes) of the destination buffer
  * @dst_tiling: Tiling mode of the destination buffer
@@ -602,6 +606,7 @@  void igt_blitter_fast_copy__raw(int fd,
 
 				/* size */
 				unsigned int width, unsigned int height,
+				uint32_t color_depth,
 
 				/* dst */
 				uint32_t dst_handle,
@@ -620,7 +625,7 @@  void igt_blitter_fast_copy__raw(int fd,
 	src_pitch = fast_copy_pitch(src_stride, src_tiling);
 	dst_pitch = fast_copy_pitch(dst_stride, dst_tiling);
 	dword0 = fast_copy_dword0(src_tiling, dst_tiling);
-	dword1 = fast_copy_dword1(src_tiling, dst_tiling);
+	dword1 = fast_copy_dword1(src_tiling, dst_tiling, color_depth);
 
 #define CHECK_RANGE(x)	((x) >= 0 && (x) < (1 << 15))
 	assert(CHECK_RANGE(src_x) && CHECK_RANGE(src_y) &&
@@ -670,6 +675,7 @@  void igt_blitter_fast_copy__raw(int fd,
  * @src_y: source pixel y-coordination
  * @width: width of the copied rectangle
  * @height: height of the copied rectangle
+ * @color_depth: Color depth of the buffer
  * @dst: destination i-g-t buffer object
  * @dst_x: destination pixel x-coordination
  * @dst_y: destination pixel y-coordination
@@ -680,7 +686,7 @@  void igt_blitter_fast_copy__raw(int fd,
  */
 void igt_blitter_fast_copy(struct intel_batchbuffer *batch,
 			   struct igt_buf *src, unsigned src_x, unsigned src_y,
-			   unsigned width, unsigned height,
+			   unsigned width, unsigned height, uint32_t color_depth,
 			   struct igt_buf *dst, unsigned dst_x, unsigned dst_y)
 {
 	uint32_t src_pitch, dst_pitch;
@@ -689,7 +695,7 @@  void igt_blitter_fast_copy(struct intel_batchbuffer *batch,
 	src_pitch = fast_copy_pitch(src->stride, src->tiling);
 	dst_pitch = fast_copy_pitch(dst->stride, src->tiling);
 	dword0 = fast_copy_dword0(src->tiling, dst->tiling);
-	dword1 = fast_copy_dword1(src->tiling, dst->tiling);
+	dword1 = fast_copy_dword1(src->tiling, dst->tiling, color_depth);
 
 #define CHECK_RANGE(x)	((x) >= 0 && (x) < (1 << 15))
 	assert(CHECK_RANGE(src_x) && CHECK_RANGE(src_y) &&
diff --git a/lib/intel_batchbuffer.h b/lib/intel_batchbuffer.h
index 62c8396..0e0feed 100644
--- a/lib/intel_batchbuffer.h
+++ b/lib/intel_batchbuffer.h
@@ -226,7 +226,7 @@  unsigned igt_buf_height(struct igt_buf *buf);
 
 void igt_blitter_fast_copy(struct intel_batchbuffer *batch,
 			  struct igt_buf *src, unsigned src_x, unsigned src_y,
-			  unsigned width, unsigned height,
+			  unsigned width, unsigned height, uint32_t color_depth,
 			  struct igt_buf *dst, unsigned dst_x, unsigned dst_y);
 
 void igt_blitter_fast_copy__raw(int fd,
@@ -238,6 +238,7 @@  void igt_blitter_fast_copy__raw(int fd,
 
 				/* size */
 				unsigned int width, unsigned int height,
+				uint32_t color_depth,
 
 				/* dst */
 				uint32_t dst_handle,
diff --git a/lib/intel_reg.h b/lib/intel_reg.h
index 0ffa803..9f3de7a 100644
--- a/lib/intel_reg.h
+++ b/lib/intel_reg.h
@@ -2530,6 +2530,7 @@  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 /* dword 1 */
 #define   XY_FAST_COPY_SRC_TILING_Yf			(1 <<  31)
 #define   XY_FAST_COPY_DST_TILING_Yf			(1 <<  30)
+#define   XY_FAST_COPY_COLOR_DEPTH_8 			(0  << 24)
 #define   XY_FAST_COPY_COLOR_DEPTH_32			(3  << 24)
 
 #define MI_STORE_DWORD_IMM		((0x20<<23)|2)
diff --git a/lib/ioctl_wrappers.c b/lib/ioctl_wrappers.c
index a269d0f..93717a0 100644
--- a/lib/ioctl_wrappers.c
+++ b/lib/ioctl_wrappers.c
@@ -54,6 +54,7 @@ 
 #include "intel_chipset.h"
 #include "intel_io.h"
 #include "igt_debugfs.h"
+#include "igt_fb.h"
 #include "config.h"
 
 #include "ioctl_wrappers.h"
@@ -1221,7 +1222,7 @@  void igt_require_fb_modifiers(int fd)
 
 int __kms_addfb(int fd, uint32_t handle, uint32_t width, uint32_t height,
 		uint32_t stride, uint32_t pixel_format, uint64_t modifier,
-		uint32_t flags, uint32_t *buf_id)
+		uint32_t uv_offset, uint32_t flags, uint32_t *buf_id)
 {
 	struct local_drm_mode_fb_cmd2 f;
 	int ret;
@@ -1238,6 +1239,13 @@  int __kms_addfb(int fd, uint32_t handle, uint32_t width, uint32_t height,
 	f.pitches[0] = stride;
 	f.modifier[0] = modifier;
 
+	if (pixel_format == DRM_FORMAT_NV12) {
+		f.handles[1] = handle;
+		f.pitches[1] = stride;
+		f.offsets[1] = uv_offset;
+		f.modifier[1] = modifier;
+	}
+
 	ret = drmIoctl(fd, LOCAL_DRM_IOCTL_MODE_ADDFB2, &f);
 
 	*buf_id = f.fb_id;
diff --git a/lib/ioctl_wrappers.h b/lib/ioctl_wrappers.h
index bc5d4bd..3d93a5e 100644
--- a/lib/ioctl_wrappers.h
+++ b/lib/ioctl_wrappers.h
@@ -175,6 +175,6 @@  void igt_require_fb_modifiers(int fd);
  */
 int __kms_addfb(int fd, uint32_t handle, uint32_t width, uint32_t height,
 		uint32_t stride, uint32_t pixel_format, uint64_t modifier,
-		uint32_t flags, uint32_t *buf_id);
+		uint32_t uv_offset, uint32_t flags, uint32_t *buf_id);
 
 #endif /* IOCTL_WRAPPERS_H */
diff --git a/tests/kms_render.c b/tests/kms_render.c
index 1759eaa..4a7aaec 100644
--- a/tests/kms_render.c
+++ b/tests/kms_render.c
@@ -78,8 +78,8 @@  static void gpu_blit(struct igt_fb *dst_fb, struct igt_fb *src_fb)
 
 	igt_assert(dst_fb->drm_format == src_fb->drm_format);
 	igt_assert(src_fb->drm_format == DRM_FORMAT_RGB565 ||
-	       igt_drm_format_to_bpp(src_fb->drm_format) != 16);
-	bpp = igt_drm_format_to_bpp(src_fb->drm_format);
+	       igt_drm_format_to_bpp(src_fb->drm_format, 0) != 16);
+	bpp = igt_drm_format_to_bpp(src_fb->drm_format, 0);
 	dst_bo = gem_handle_to_libdrm_bo(bufmgr, drm_fd, "destination",
 					 dst_fb->gem_handle);
 	igt_assert(dst_bo);