/*
 * Copyright © 2009 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Xiang Haihao <haihao.xiang@intel.com>
 *    Zou Nan hai <nanhai.zou@intel.com>
 *
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <va/va_backend.h>

#include "intel_batchbuffer.h"
#include "intel_driver.h"

#include "i965_defines.h"
#include "i965_media.h"
#include "i965_media_mpeg2.h"
#include "i965_drv_video.h"

#define SURFACE_TARGET	    0
#define SURFACE_FORWARD	    1
#define SURFACE_BACKWARD    2
#define SURFACE_BIDIRECT    3

enum interface {
    FRAME_INTRA = 0,
    FRAME_FRAME_PRED_FORWARD,
    FRAME_FRAME_PRED_BACKWARD,
    FRAME_FRAME_PRED_BIDIRECT,
    FRAME_FIELD_PRED_FORWARD,
    FRAME_FIELD_PRED_BACKWARD,
    FRAME_FIELD_PRED_BIDIRECT,
    LIB_INTERFACE,
    FIELD_INTRA,
    FIELD_FORWARD,
    FIELD_FORWARD_16X8,
    FIELD_BACKWARD,
    FIELD_BACKWARD_16X8,
    FIELD_BIDIRECT,
    FIELD_BIDIRECT_16X8
};

/* idct table */
#define C0 23170
#define C1 22725
#define C2 21407
#define C3 19266
#define C4 16383
#define C5 12873
#define C6 8867
#define C7 4520
const uint32_t idct_table[] = {
    C4, C1, C2, C3, C4, C5, C6, C7,             //g5
    C4, C1, C2, C3, C4, C5, C6, C7,
    C4, C3, C6,-C7,-C4,-C1,-C2,-C5,
    C4, C3, C6,-C7,-C4,-C1,-C2,-C5,
    C4, C5,-C6,-C1,-C4, C7, C2, C3,
    C4, C5,-C6,-C1,-C4, C7, C2, C3,
    C4, C7,-C2,-C5, C4, C3,-C6,-C1,
    C4, C7,-C2,-C5, C4, C3,-C6,-C1,
    C4,-C7,-C2, C5, C4,-C3,-C6, C1,
    C4,-C7,-C2, C5, C4,-C3,-C6, C1,
    C4,-C5,-C6, C1,-C4,-C7, C2,-C3,
    C4,-C5,-C6, C1,-C4,-C7, C2,-C3,
    C4,-C3, C6, C7,-C4, C1,-C2, C5,
    C4,-C3, C6, C7,-C4, C1,-C2, C5,
    C4,-C1, C2,-C3, C4,-C5, C6,-C7,
    C4,-C1, C2,-C3, C4,-C5, C6,-C7              //g20
};
#undef C0
#undef C1
#undef C2
#undef C3
#undef C4
#undef C5
#undef C6
#undef C7

const uint32_t zigzag_direct[64] = {
    0,   1,  8, 16,  9,  2,  3, 10,
    17, 24, 32, 25, 18, 11,  4,  5,
    12, 19, 26, 33, 40, 48, 41, 34,
    27, 20, 13,  6,  7, 14, 21, 28,
    35, 42, 49, 56, 57, 50, 43, 36,
    29, 22, 15, 23, 30, 37, 44, 51,
    58, 59, 52, 45, 38, 31, 39, 46,
    53, 60, 61, 54, 47, 55, 62, 63
};

static uint32_t frame_intra_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_intra.g4b"
};
static uint32_t frame_frame_pred_forward_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_frame_pred_forward.g4b"
};
static uint32_t frame_frame_pred_backward_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_frame_pred_backward.g4b"
};
static uint32_t frame_frame_pred_bidirect_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_frame_pred_bidirect.g4b"
};
static uint32_t frame_field_pred_forward_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_field_pred_forward.g4b"
};
static uint32_t frame_field_pred_backward_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_field_pred_backward.g4b"
};
static uint32_t frame_field_pred_bidirect_kernel[][4] = {
   #include "shaders/mpeg2/vld/frame_field_pred_bidirect.g4b"
};
static uint32_t lib_kernel[][4] = {
   #include "shaders/mpeg2/vld/lib.g4b"
};
/*field picture*/
static uint32_t field_intra_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_intra.g4b"
};
static uint32_t field_forward_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_forward.g4b"
};
static uint32_t field_forward_16x8_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_forward_16x8.g4b"
};
static uint32_t field_backward_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_backward.g4b"
};
static uint32_t field_backward_16x8_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_backward_16x8.g4b"
};
static uint32_t field_bidirect_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_bidirect.g4b"
};
static uint32_t field_bidirect_16x8_kernel[][4] = {
   #include "shaders/mpeg2/vld/field_bidirect_16x8.g4b"
};

static struct media_kernel  mpeg2_vld_kernels_gen4[] = {
    {
        "FRAME_INTRA",
        FRAME_INTRA,
        frame_intra_kernel, 
        sizeof(frame_intra_kernel),
        NULL
    },

    {
        "FRAME_FRAME_PRED_FORWARD",
    	FRAME_FRAME_PRED_FORWARD,
        frame_frame_pred_forward_kernel, 
        sizeof(frame_frame_pred_forward_kernel),
        NULL
    },

    {
        "FRAME_FRAME_PRED_BACKWARD",
        FRAME_FRAME_PRED_BACKWARD,
        frame_frame_pred_backward_kernel, 
        sizeof(frame_frame_pred_backward_kernel),
        NULL
    },

    {   
        "FRAME_FRAME_PRED_BIDIRECT",
        FRAME_FRAME_PRED_BIDIRECT,
        frame_frame_pred_bidirect_kernel, 
        sizeof(frame_frame_pred_bidirect_kernel),
        NULL
    },

    {
        "FRAME_FIELD_PRED_FORWARD",
        FRAME_FIELD_PRED_FORWARD,
        frame_field_pred_forward_kernel, 
        sizeof(frame_field_pred_forward_kernel),
        NULL
    },

    {
        "FRAME_FIELD_PRED_BACKWARD",
        FRAME_FIELD_PRED_BACKWARD,
        frame_field_pred_backward_kernel, 
        sizeof(frame_field_pred_backward_kernel),
        NULL
    },

    {
        "FRAME_FIELD_PRED_BIDIRECT",
        FRAME_FIELD_PRED_BIDIRECT,
        frame_field_pred_bidirect_kernel, 
        sizeof(frame_field_pred_bidirect_kernel),
        NULL
    },
    
    {   
        "LIB",
        LIB_INTERFACE,
        lib_kernel, 
        sizeof(lib_kernel),
        NULL
    },

    {
        "FIELD_INTRA",
        FIELD_INTRA,
        field_intra_kernel, 
        sizeof(field_intra_kernel),
        NULL
    },

    {
        "FIELD_FORWARD",
        FIELD_FORWARD,
        field_forward_kernel, 
        sizeof(field_forward_kernel),
        NULL
    },

    {
        "FIELD_FORWARD_16X8",
        FIELD_FORWARD_16X8,
        field_forward_16x8_kernel, 
        sizeof(field_forward_16x8_kernel),
        NULL
    },

    {
        "FIELD_BACKWARD",
        FIELD_BACKWARD,
        field_backward_kernel, 
        sizeof(field_backward_kernel),
        NULL
    },

    {
        "FIELD_BACKWARD_16X8",
        FIELD_BACKWARD_16X8,
        field_backward_16x8_kernel, 
        sizeof(field_backward_16x8_kernel),
        NULL
    },

    {
        "FIELD_BIDIRECT",
        FIELD_BIDIRECT,
        field_bidirect_kernel, 
        sizeof(field_bidirect_kernel),
        NULL
    },

    {
        "FIELD_BIDIRECT_16X8",
        FIELD_BIDIRECT_16X8,
        field_bidirect_16x8_kernel, 
        sizeof(field_bidirect_16x8_kernel),
        NULL
    }
};

/* On IRONLAKE */
static uint32_t frame_intra_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_intra.g4b.gen5"
};
static uint32_t frame_frame_pred_forward_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_frame_pred_forward.g4b.gen5"
};
static uint32_t frame_frame_pred_backward_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_frame_pred_backward.g4b.gen5"
};
static uint32_t frame_frame_pred_bidirect_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_frame_pred_bidirect.g4b.gen5"
};
static uint32_t frame_field_pred_forward_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_field_pred_forward.g4b.gen5"
};
static uint32_t frame_field_pred_backward_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_field_pred_backward.g4b.gen5"
};
static uint32_t frame_field_pred_bidirect_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/frame_field_pred_bidirect.g4b.gen5"
};
static uint32_t lib_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/lib.g4b.gen5"
};
/*field picture*/
static uint32_t field_intra_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_intra.g4b.gen5"
};
static uint32_t field_forward_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_forward.g4b.gen5"
};
static uint32_t field_forward_16x8_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_forward_16x8.g4b.gen5"
};
static uint32_t field_backward_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_backward.g4b.gen5"
};
static uint32_t field_backward_16x8_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_backward_16x8.g4b.gen5"
};
static uint32_t field_bidirect_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_bidirect.g4b.gen5"
};
static uint32_t field_bidirect_16x8_kernel_gen5[][4] = {
   #include "shaders/mpeg2/vld/field_bidirect_16x8.g4b.gen5"
};

static struct media_kernel  mpeg2_vld_kernels_gen5[] = {
    {
        "FRAME_INTRA",
        FRAME_INTRA,
        frame_intra_kernel_gen5, 
        sizeof(frame_intra_kernel_gen5),
        NULL
    },

    {
        "FRAME_FRAME_PRED_FORWARD",
    	FRAME_FRAME_PRED_FORWARD,
        frame_frame_pred_forward_kernel_gen5, 
        sizeof(frame_frame_pred_forward_kernel_gen5),
        NULL
    },

    {
        "FRAME_FRAME_PRED_BACKWARD",
        FRAME_FRAME_PRED_BACKWARD,
        frame_frame_pred_backward_kernel_gen5, 
        sizeof(frame_frame_pred_backward_kernel_gen5),
        NULL
    },

    {   
        "FRAME_FRAME_PRED_BIDIRECT",
        FRAME_FRAME_PRED_BIDIRECT,
        frame_frame_pred_bidirect_kernel_gen5, 
        sizeof(frame_frame_pred_bidirect_kernel_gen5),
        NULL
    },

    {
        "FRAME_FIELD_PRED_FORWARD",
        FRAME_FIELD_PRED_FORWARD,
        frame_field_pred_forward_kernel_gen5, 
        sizeof(frame_field_pred_forward_kernel_gen5),
        NULL
    },

    {
        "FRAME_FIELD_PRED_BACKWARD",
        FRAME_FIELD_PRED_BACKWARD,
        frame_field_pred_backward_kernel_gen5, 
        sizeof(frame_field_pred_backward_kernel_gen5),
        NULL
    },

    {
        "FRAME_FIELD_PRED_BIDIRECT",
        FRAME_FIELD_PRED_BIDIRECT,
        frame_field_pred_bidirect_kernel_gen5, 
        sizeof(frame_field_pred_bidirect_kernel_gen5),
        NULL
    },
    
    {   
        "LIB",
        LIB_INTERFACE,
        lib_kernel_gen5, 
        sizeof(lib_kernel_gen5),
        NULL
    },

    {
        "FIELD_INTRA",
        FIELD_INTRA,
        field_intra_kernel_gen5, 
        sizeof(field_intra_kernel_gen5),
        NULL
    },

    {
        "FIELD_FORWARD",
        FIELD_FORWARD,
        field_forward_kernel_gen5, 
        sizeof(field_forward_kernel_gen5),
        NULL
    },

    {
        "FIELD_FORWARD_16X8",
        FIELD_FORWARD_16X8,
        field_forward_16x8_kernel_gen5, 
        sizeof(field_forward_16x8_kernel_gen5),
        NULL
    },

    {
        "FIELD_BACKWARD",
        FIELD_BACKWARD,
        field_backward_kernel_gen5, 
        sizeof(field_backward_kernel_gen5),
        NULL
    },

    {
        "FIELD_BACKWARD_16X8",
        FIELD_BACKWARD_16X8,
        field_backward_16x8_kernel_gen5, 
        sizeof(field_backward_16x8_kernel_gen5),
        NULL
    },

    {
        "FIELD_BIDIRECT",
        FIELD_BIDIRECT,
        field_bidirect_kernel_gen5, 
        sizeof(field_bidirect_kernel_gen5),
        NULL
    },

    {
        "FIELD_BIDIRECT_16X8",
        FIELD_BIDIRECT_16X8,
        field_bidirect_16x8_kernel_gen5, 
        sizeof(field_bidirect_16x8_kernel_gen5),
        NULL
    }
};

static struct media_kernel  *mpeg2_vld_kernels = NULL;

#define NUM_MPEG2_VLD_KERNELS (sizeof(mpeg2_vld_kernels_gen4)/sizeof(mpeg2_vld_kernels_gen4[0]))

static void
i965_media_mpeg2_surface_state(VADriverContextP ctx, 
                               int index,
                               struct object_surface *obj_surface,
                               unsigned long offset, 
                               int w, int h,
                               Bool is_dst,
			       int vert_line_stride,
			       int vert_line_stride_ofs)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);  
    struct i965_media_state *media_state = &i965->media_state;
    struct i965_surface_state *ss;
    dri_bo *bo;
    uint32_t write_domain, read_domain;

    bo = dri_bo_alloc(i965->intel.bufmgr, 
                      "surface state", 
                      sizeof(struct i965_surface_state), 32);
    assert(bo);
    dri_bo_map(bo, 1);
    assert(bo->virtual);
    ss = bo->virtual;
    memset(ss, 0, sizeof(*ss));
    ss->ss0.surface_type = I965_SURFACE_2D;
    ss->ss0.surface_format = I965_SURFACEFORMAT_R8_SINT;
    ss->ss0.vert_line_stride = vert_line_stride;
    ss->ss0.vert_line_stride_ofs = vert_line_stride_ofs;
    ss->ss1.base_addr = obj_surface->bo->offset + offset;
    ss->ss2.width = w - 1;
    ss->ss2.height = h - 1;
    ss->ss3.pitch = w - 1;

    if (is_dst) {
        write_domain = I915_GEM_DOMAIN_RENDER;
        read_domain = I915_GEM_DOMAIN_RENDER;
    } else {
        write_domain = 0;
        read_domain = I915_GEM_DOMAIN_SAMPLER;
    }

    dri_bo_emit_reloc(bo,
                      read_domain, write_domain,
                      offset,
                      offsetof(struct i965_surface_state, ss1),
                      obj_surface->bo);
    dri_bo_unmap(bo);

    assert(index < MAX_MEDIA_SURFACES);
//    assert(media_state->surface_state[index].bo == NULL);
    media_state->surface_state[index].bo = bo;
}

static void
i965_media_mpeg2_surface_setup(VADriverContextP ctx, 
                               int base_index,
                               struct object_surface *obj_surface, 
                               Bool is_dst, 
			       int picture_structure,
			       int surface)
{
    int w = obj_surface->width;
    int h = obj_surface->height;

    if (obj_surface->bo == NULL) {
        struct i965_driver_data *i965 = i965_driver_data(ctx);  
        
        obj_surface->bo = dri_bo_alloc(i965->intel.bufmgr,
                                       "vaapi surface",
                                       obj_surface->size,
                                       0x1000);
    }

    if (picture_structure == MPEG_FRAME) {
	i965_media_mpeg2_surface_state(ctx, base_index + 0, obj_surface,
					0, w, h, 
					is_dst, 0, 0);
	i965_media_mpeg2_surface_state(ctx, base_index + 1, obj_surface,
					w * h, w / 2, h / 2, 
					is_dst, 0, 0);
	i965_media_mpeg2_surface_state(ctx, base_index + 2, obj_surface,
					w * h + w * h / 4, w / 2, h / 2, 
					is_dst, 0, 0);
    } else {
	if (surface == SURFACE_TARGET) {
	    i965_media_mpeg2_surface_state(ctx, 3, obj_surface,
					    0, w, h, 
					    False, 0, 0);
	    i965_media_mpeg2_surface_state(ctx, 10, obj_surface,
					    w * h, w / 2, h / 2, 
					    False, 0, 0);
	    i965_media_mpeg2_surface_state(ctx, 11, obj_surface,
					    w * h + w * h / 4, w / 2, h / 2, 
					    False, 0, 0);
	    if (picture_structure == MPEG_TOP_FIELD) {
		i965_media_mpeg2_surface_state(ctx, base_index + 0, obj_surface,
						0, w, h, 
						True, 1, 0);
		i965_media_mpeg2_surface_state(ctx, base_index + 1, obj_surface,
						w * h, w / 2, h / 2, 
						True, 1, 0);
		i965_media_mpeg2_surface_state(ctx, base_index + 2, obj_surface,
						w * h + w * h / 4, w / 2, h / 2, 
						True, 1, 0);
	    } else {
		assert(picture_structure == MPEG_BOTTOM_FIELD);
		i965_media_mpeg2_surface_state(ctx, base_index + 0, obj_surface,
						0, w, h, 
						True, 1, 1);
		i965_media_mpeg2_surface_state(ctx, base_index + 1, obj_surface,
						w * h, w / 2, h / 2, 
						True, 1, 1);
		i965_media_mpeg2_surface_state(ctx, base_index + 2, obj_surface,
						w * h + w * h / 4, w / 2, h / 2, 
						True, 1, 1);
	    }
	} else {
	    i965_media_mpeg2_surface_state(ctx, base_index + 0, obj_surface,
					    0, w, h, 
					    is_dst, 0, 0);
	    i965_media_mpeg2_surface_state(ctx, base_index + 1, obj_surface,
					    w * h, w / 2, h / 2, 
					    is_dst, 0, 0);
	    i965_media_mpeg2_surface_state(ctx, base_index + 2, obj_surface,
					    w * h + w * h / 4, w / 2, h / 2, 
					    is_dst, 0, 0);
	}
    }
}

void 
i965_media_mpeg2_surfaces_setup(VADriverContextP ctx, 
                                struct decode_state *decode_state)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);  
    struct object_surface *obj_surface;
    VAPictureParameterBufferMPEG2 *param;

    assert(decode_state->pic_param && decode_state->pic_param->buffer);
    param = (VAPictureParameterBufferMPEG2 *)decode_state->pic_param->buffer;

    obj_surface = SURFACE(decode_state->current_render_target);
    assert(obj_surface);
    i965_media_mpeg2_surface_setup(ctx, 0, obj_surface, True,
		param->picture_coding_extension.bits.picture_structure, SURFACE_TARGET);

    obj_surface = SURFACE(param->forward_reference_picture);
    if (!obj_surface) {
//        assert(param->picture_coding_type == 1); /* I-picture */
    } else {
        i965_media_mpeg2_surface_setup(ctx, 4, obj_surface, False,
			param->picture_coding_extension.bits.picture_structure, SURFACE_FORWARD);
        obj_surface = SURFACE(param->backward_reference_picture);
        if (!obj_surface) {
            assert(param->picture_coding_type == 2); /* P-picture */

            obj_surface = SURFACE(param->forward_reference_picture);
            i965_media_mpeg2_surface_setup(ctx, 7, obj_surface, False,
				param->picture_coding_extension.bits.picture_structure, SURFACE_BACKWARD);            
        } else {
            assert(param->picture_coding_type == 3); /* B-picture */
            i965_media_mpeg2_surface_setup(ctx, 7, obj_surface, False,
				param->picture_coding_extension.bits.picture_structure, SURFACE_BIDIRECT);
        }
    }
}

static void
i965_media_mpeg2_binding_table(VADriverContextP ctx)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);
    struct i965_media_state *media_state = &i965->media_state;
    int i;
    unsigned int *binding_table;
    dri_bo *bo = media_state->binding_table.bo;

    dri_bo_map(bo, 1);
    assert(bo->virtual);
    binding_table = bo->virtual;
    memset(binding_table, 0, bo->size);

    for (i = 0; i < MAX_MEDIA_SURFACES; i++) {
        if (media_state->surface_state[i].bo) {
            binding_table[i] = media_state->surface_state[i].bo->offset;
            dri_bo_emit_reloc(bo,
                              I915_GEM_DOMAIN_INSTRUCTION, 0,
                              0,
                              i * sizeof(*binding_table),
                              media_state->surface_state[i].bo);
        }
    }

    dri_bo_unmap(media_state->binding_table.bo);
}

static void
i965_media_mpeg2_vfe_state(VADriverContextP ctx)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);
    struct i965_media_state *media_state = &i965->media_state;
    struct i965_vfe_state *vfe_state;
    dri_bo *bo;

    bo = media_state->vfe_state.bo;
    dri_bo_map(bo, 1);
    assert(bo->virtual);
    vfe_state = bo->virtual;
    memset(vfe_state, 0, sizeof(*vfe_state));
    vfe_state->vfe0.extend_vfe_state_present = 1;
    vfe_state->vfe1.vfe_mode = VFE_VLD_MODE;
    vfe_state->vfe1.num_urb_entries = media_state->urb.num_vfe_entries;
    vfe_state->vfe1.children_present = 0;
    vfe_state->vfe1.urb_entry_alloc_size = media_state->urb.size_vfe_entry - 1;
    vfe_state->vfe1.max_threads = media_state->urb.num_vfe_entries - 1;
    vfe_state->vfe2.interface_descriptor_base = 
        media_state->idrt.bo->offset >> 4; /* reloc */
    dri_bo_emit_reloc(bo,
                      I915_GEM_DOMAIN_INSTRUCTION, 0,
                      0,
                      offsetof(struct i965_vfe_state, vfe2),
                      media_state->idrt.bo);
    dri_bo_unmap(bo);
}

static void 
i965_media_mpeg2_interface_descriptor_remap_table(VADriverContextP ctx)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);
    struct i965_media_state *media_state = &i965->media_state;
    struct i965_interface_descriptor *desc;
    int i;
    dri_bo *bo;

    bo = media_state->idrt.bo;
    dri_bo_map(bo, 1);
    assert(bo->virtual);
    desc = bo->virtual;

    for (i = 0; i < NUM_MPEG2_VLD_KERNELS; i++) {
        memset(desc, 0, sizeof(*desc));
        desc->desc0.grf_reg_blocks = 15;
        desc->desc0.kernel_start_pointer = mpeg2_vld_kernels[i].bo->offset >> 6; /* reloc */
        desc->desc1.const_urb_entry_read_offset = 0;
        desc->desc1.const_urb_entry_read_len = 30;
        desc->desc3.binding_table_entry_count = 0;
        desc->desc3.binding_table_pointer = 
            media_state->binding_table.bo->offset >> 5; /*reloc */

        dri_bo_emit_reloc(bo,
                          I915_GEM_DOMAIN_INSTRUCTION, 0,
                          desc->desc0.grf_reg_blocks,
                          i * sizeof(*desc) + offsetof(struct i965_interface_descriptor, desc0),
                          mpeg2_vld_kernels[i].bo);

        dri_bo_emit_reloc(bo,
                          I915_GEM_DOMAIN_INSTRUCTION, 0,
                          desc->desc3.binding_table_entry_count,
                          i * sizeof(*desc) + offsetof(struct i965_interface_descriptor, desc3),
                          media_state->binding_table.bo);
        desc++;
    }

    dri_bo_unmap(bo);
}

void
i965_media_mpeg2_vld_state(VADriverContextP ctx, struct decode_state *decode_state)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);
    struct i965_media_state *media_state = &i965->media_state;
    struct i965_vld_state *vld_state;
    VAPictureParameterBufferMPEG2 *param;

    assert(decode_state->pic_param && decode_state->pic_param->buffer);
    param = (VAPictureParameterBufferMPEG2 *)decode_state->pic_param->buffer;

    assert(media_state->extended_state.bo);
    dri_bo_map(media_state->extended_state.bo, 1);
    assert(media_state->extended_state.bo->virtual);
    vld_state = media_state->extended_state.bo->virtual;
    memset(vld_state, 0, sizeof(*vld_state));

    vld_state->vld0.f_code_0_0 = ((param->f_code >> 12) & 0xf);
    vld_state->vld0.f_code_0_1 = ((param->f_code >> 8) & 0xf);
    vld_state->vld0.f_code_1_0 = ((param->f_code >> 4) & 0xf);
    vld_state->vld0.f_code_1_1 = (param->f_code & 0xf);
    vld_state->vld0.intra_dc_precision = param->picture_coding_extension.bits.intra_dc_precision;
    vld_state->vld0.picture_structure = param->picture_coding_extension.bits.picture_structure;
    vld_state->vld0.top_field_first = param->picture_coding_extension.bits.top_field_first;
    vld_state->vld0.frame_predict_frame_dct = param->picture_coding_extension.bits.frame_pred_frame_dct;
    vld_state->vld0.concealment_motion_vector = param->picture_coding_extension.bits.concealment_motion_vectors;
    vld_state->vld0.quantizer_scale_type = param->picture_coding_extension.bits.q_scale_type;
    vld_state->vld0.intra_vlc_format = param->picture_coding_extension.bits.intra_vlc_format;
    vld_state->vld0.scan_order = param->picture_coding_extension.bits.alternate_scan;

    vld_state->vld1.picture_coding_type = param->picture_coding_type;

    if (vld_state->vld0.picture_structure == MPEG_FRAME) {
	/*frame picture*/ 
	vld_state->desc_remap_table0.index_0 = FRAME_INTRA;
	vld_state->desc_remap_table0.index_1 = FRAME_FRAME_PRED_FORWARD;
	vld_state->desc_remap_table0.index_2 = FRAME_FIELD_PRED_FORWARD;
	vld_state->desc_remap_table0.index_3 = FRAME_FIELD_PRED_BIDIRECT; /* dual prime */
	vld_state->desc_remap_table0.index_4 = FRAME_FRAME_PRED_BACKWARD;
	vld_state->desc_remap_table0.index_5 = FRAME_FIELD_PRED_BACKWARD;
	vld_state->desc_remap_table0.index_6 = FRAME_FRAME_PRED_BIDIRECT;
	vld_state->desc_remap_table0.index_7 = FRAME_FIELD_PRED_BIDIRECT;

	vld_state->desc_remap_table1.index_8 = FRAME_INTRA;
	vld_state->desc_remap_table1.index_9 = FRAME_FRAME_PRED_FORWARD;
	vld_state->desc_remap_table1.index_10 = FRAME_FIELD_PRED_FORWARD;
	vld_state->desc_remap_table1.index_11 = FRAME_FIELD_PRED_BIDIRECT;
	vld_state->desc_remap_table1.index_12 = FRAME_FRAME_PRED_BACKWARD;
	vld_state->desc_remap_table1.index_13 = FRAME_FIELD_PRED_BACKWARD;
	vld_state->desc_remap_table1.index_14 = FRAME_FRAME_PRED_BIDIRECT;
	vld_state->desc_remap_table1.index_15 = FRAME_FIELD_PRED_BIDIRECT;
    } else {
	/*field picture*/
	vld_state->desc_remap_table0.index_0 = FIELD_INTRA;
	vld_state->desc_remap_table0.index_1 = FIELD_FORWARD;
	vld_state->desc_remap_table0.index_2 = FIELD_FORWARD_16X8;
	vld_state->desc_remap_table0.index_3 = FIELD_BIDIRECT; /* dual prime */
	vld_state->desc_remap_table0.index_4 = FIELD_BACKWARD;
	vld_state->desc_remap_table0.index_5 = FIELD_BACKWARD_16X8;
	vld_state->desc_remap_table0.index_6 = FIELD_BIDIRECT;
	vld_state->desc_remap_table0.index_7 = FIELD_BIDIRECT_16X8;
    }

    dri_bo_unmap(media_state->extended_state.bo);
}

static void
i965_media_mpeg2_upload_constants(VADriverContextP ctx, struct decode_state *decode_state)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);
    struct i965_media_state *media_state = &i965->media_state;
    int i, j;
    unsigned char *constant_buffer;
    unsigned char *qmx;
    unsigned int *lib_reloc;
    int lib_reloc_offset = 0;

    dri_bo_map(media_state->curbe.bo, 1);
    assert(media_state->curbe.bo->virtual);
    constant_buffer = media_state->curbe.bo->virtual;

    /* iq_matrix */
    if (decode_state->iq_matrix && decode_state->iq_matrix->buffer) {
        VAIQMatrixBufferMPEG2 *iq_matrix = (VAIQMatrixBufferMPEG2 *)decode_state->iq_matrix->buffer;

        /* Upload quantisation matrix in row-major order. The mplayer vaapi
         * patch passes quantisation matrix in zig-zag order to va library.
         * Do we need a flag in VAIQMatrixBufferMPEG2 to specify the order
         * of the quantisation matrix?
         */
        qmx = constant_buffer;
        if (iq_matrix->load_intra_quantiser_matrix) {
            for (i = 0; i < 64; i++) {
                j = zigzag_direct[i];
                qmx[j] = iq_matrix->intra_quantiser_matrix[i];
            }
        }

        qmx = constant_buffer + 64;
        if (iq_matrix->load_non_intra_quantiser_matrix) {
            for (i = 0; i < 64; i++) {
                j = zigzag_direct[i];
                qmx[j] = iq_matrix->non_intra_quantiser_matrix[i];
            }
        }

        /* no chroma quantisation matrices for 4:2:0 data */
    }

    /* idct table */
    memcpy(constant_buffer + 128, idct_table, sizeof(idct_table));

    /* idct lib reloc */
    lib_reloc_offset = 128 + sizeof(idct_table);
    lib_reloc = (unsigned int *)(constant_buffer + lib_reloc_offset);
    for (i = 0; i < 8; i++) {
        lib_reloc[i] = mpeg2_vld_kernels[LIB_INTERFACE].bo->offset;
        dri_bo_emit_reloc(media_state->curbe.bo,
                          I915_GEM_DOMAIN_INSTRUCTION, 0,
                          0,
                          lib_reloc_offset + i * sizeof(unsigned int),
                          mpeg2_vld_kernels[LIB_INTERFACE].bo);
    }

    dri_bo_unmap(media_state->curbe.bo);
}

static void
i965_media_mpeg2_states_setup(VADriverContextP ctx, struct decode_state *decode_state)
{
    i965_media_mpeg2_surfaces_setup(ctx, decode_state);
    i965_media_mpeg2_binding_table(ctx);
    i965_media_mpeg2_interface_descriptor_remap_table(ctx);
    i965_media_mpeg2_vld_state(ctx, decode_state);
    i965_media_mpeg2_vfe_state(ctx);
    i965_media_mpeg2_upload_constants(ctx, decode_state);
}

static void
i965_media_mpeg2_objects(VADriverContextP ctx, struct decode_state *decode_state)
{
    int i, j;
    VASliceParameterBufferMPEG2 *slice_param;

    for (j = 0; j < decode_state->num_slice_params; j++) {
        assert(decode_state->slice_params[j] && decode_state->slice_params[j]->buffer);
        assert(decode_state->slice_datas[j] && decode_state->slice_datas[j]->bo);
        slice_param = (VASliceParameterBufferMPEG2 *)decode_state->slice_params[j]->buffer;

        for (i = 0; i < decode_state->slice_params[j]->num_elements; i++) {
            assert(slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_ALL);

            BEGIN_BATCH(ctx, 6);
            OUT_BATCH(ctx, CMD_MEDIA_OBJECT | 4);
            OUT_BATCH(ctx, 0);
            OUT_BATCH(ctx, slice_param->slice_data_size - (slice_param->macroblock_offset >> 3));
            OUT_RELOC(ctx, decode_state->slice_datas[j]->bo, 
                      I915_GEM_DOMAIN_SAMPLER, 0, 
                      slice_param->slice_data_offset + (slice_param->macroblock_offset >> 3));
            OUT_BATCH(ctx, 
                      ((slice_param->slice_horizontal_position << 24) |     
                       (slice_param->slice_vertical_position << 16) |
                       (127 << 8) | 
                       (slice_param->macroblock_offset & 0x7)));
            OUT_BATCH(ctx, slice_param->quantiser_scale_code << 24);
            ADVANCE_BATCH(ctx);          
            slice_param++;
        }
    }
}

static void
i965_media_mpeg2_free_private_context(void **data)
{
    int i;

    for (i = 0; i < NUM_MPEG2_VLD_KERNELS; i++) {
        struct media_kernel *kernel = &mpeg2_vld_kernels[i];

        dri_bo_unreference(kernel->bo);
        kernel->bo = NULL;
    }

    mpeg2_vld_kernels = NULL;
}

void 
i965_media_mpeg2_decode_init(VADriverContextP ctx, struct decode_state *decode_state)
{
    struct i965_driver_data *i965 = i965_driver_data(ctx);
    struct i965_media_state *media_state = &i965->media_state;
    dri_bo *bo;
    int i;
    if (mpeg2_vld_kernels == NULL) {
        /* kernel */
        assert(NUM_MPEG2_VLD_KERNELS == (sizeof(mpeg2_vld_kernels_gen5) / 
                                         sizeof(mpeg2_vld_kernels_gen5[0])));
        assert(NUM_MPEG2_VLD_KERNELS <= MAX_INTERFACE_DESC);

        if (IS_IRONLAKE(i965->intel.device_id))
            mpeg2_vld_kernels = mpeg2_vld_kernels_gen5;
        else
            mpeg2_vld_kernels = mpeg2_vld_kernels_gen4;

        for (i = 0; i < NUM_MPEG2_VLD_KERNELS; i++) {
            struct media_kernel *kernel = &mpeg2_vld_kernels[i];
            kernel->bo = dri_bo_alloc(i965->intel.bufmgr, 
                                      kernel->name, 
                                      kernel->size, 64);
            assert(kernel->bo);
            dri_bo_subdata(kernel->bo, 0, kernel->size, kernel->bin);
        }

        /* URB */
        media_state->urb.num_vfe_entries = 28;
        media_state->urb.size_vfe_entry = 13;

        media_state->urb.num_cs_entries = 1;
        media_state->urb.size_cs_entry = 16;

        media_state->urb.vfe_start = 0;
        media_state->urb.cs_start = media_state->urb.vfe_start + 
            media_state->urb.num_vfe_entries * media_state->urb.size_vfe_entry;
        assert(media_state->urb.cs_start + 
               media_state->urb.num_cs_entries * media_state->urb.size_cs_entry <= URB_SIZE((&i965->intel)));

        /* hook functions */
        media_state->media_states_setup = i965_media_mpeg2_states_setup;
        media_state->media_objects = i965_media_mpeg2_objects;
        media_state->free_private_context = i965_media_mpeg2_free_private_context;
    }

    media_state->extended_state.enabled = 1;
    media_state->indirect_object.bo = NULL;
    dri_bo_unreference(media_state->extended_state.bo);
    bo = dri_bo_alloc(i965->intel.bufmgr, 
                      "vld state", 
                      sizeof(struct i965_vld_state), 32);
    assert(bo);
    media_state->extended_state.bo = bo;
}

