/*
 * Copyright (C) 2010 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:
 *    Zhou Chang <chang.zhou@intel.com>
 *
 */

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

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

#include "i965_defines.h"
#include "i965_drv_video.h"
#include "i965_encoder.h"
#include "gen6_vme.h"
#include "gen6_mfc.h"

#include "i965_post_processing.h"
#include "i965_encoder_api.h"
#include "i965_avc_encoder_common.h"

static struct intel_fraction
reduce_fraction(struct intel_fraction f)
{
	unsigned int a = f.num, b = f.den, c;
	while ((c = a % b)) {
		a = b;
		b = c;
	}
	return (struct intel_fraction) {
		f.num / b, f.den / b
	};
}

static VAStatus
clear_border(struct object_surface *obj_surface)
{
	int width[3], height[3], hstride[3], vstride[3]; /* in byte */
	int planes;
	unsigned char* p;
	int i, j;

	if (obj_surface->border_cleared)
		return VA_STATUS_SUCCESS;

	if (obj_surface->fourcc == VA_FOURCC_NV12) {
		planes = 2;
		width[0] = width[1] = obj_surface->orig_width;
		height[0] = obj_surface->orig_height;
		height[1] = obj_surface->orig_height / 2;
		hstride[0] = hstride[1] = obj_surface->width;
		vstride[0] = obj_surface->height;
		vstride[1] = obj_surface->height / 2;

	} else {
		/* todo add P010 */
		return VA_STATUS_SUCCESS;
	}
	drm_intel_gem_bo_map_gtt(obj_surface->bo);

	p = (unsigned char*)obj_surface->bo->virtual;
	if (!p)
		return VA_STATUS_ERROR_INVALID_SURFACE;

	for (i = 0; i < planes; i++) {
		int w = width[i];
		int h = height[i];
		int hs = hstride[i];
		int vs = vstride[i];
		/* right */
		for (j = 0; j < h; j++) {
			memset(p + w, 0, hs - w);
			p += hs;
		}
		/* bottom */
		for (/* nothing */; j < vs; j++) {
			memset(p, 0, hs);
			p += hs;
		}

	}
	drm_intel_gem_bo_unmap_gtt(obj_surface->bo);
	obj_surface->border_cleared = true;
	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_yuv_surface(VADriverContextP ctx,
								VAProfile profile,
								struct encode_state *encode_state,
								struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct i965_surface src_surface, dst_surface;
	struct object_surface *obj_surface;
	VAStatus status;
	VARectangle rect;
	int format = VA_RT_FORMAT_YUV420;
	unsigned int fourcc = VA_FOURCC_NV12;

	/* releae the temporary surface */
	if (encoder_context->is_tmp_id) {
		i965_DestroySurfaces(ctx, &encoder_context->input_yuv_surface, 1);
		encode_state->input_yuv_object = NULL;
	}

	encoder_context->is_tmp_id = 0;
	obj_surface = SURFACE(encode_state->current_render_target);
	assert(obj_surface && obj_surface->bo);

	if (!obj_surface || !obj_surface->bo)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	if (VAProfileHEVCMain10 == profile &&
		obj_surface->fourcc != VA_FOURCC_P010)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	if (obj_surface->fourcc == VA_FOURCC_NV12 ||
		(VAProfileHEVCMain10 == profile &&
		 obj_surface->fourcc == VA_FOURCC_P010)) {

		unsigned int tiling = 0, swizzle = 0;
		dri_bo_get_tiling(obj_surface->bo, &tiling, &swizzle);

		if (tiling == I915_TILING_Y) {
			encoder_context->input_yuv_surface = encode_state->current_render_target;
			encode_state->input_yuv_object = obj_surface;
			return clear_border(obj_surface);
		}
	}

	if (VAProfileHEVCMain10 == profile) {
		format = VA_RT_FORMAT_YUV420_10BPP;
		fourcc = VA_FOURCC_P010;
	}

	rect.x = 0;
	rect.y = 0;
	rect.width = obj_surface->orig_width;
	rect.height = obj_surface->orig_height;

	src_surface.base = (struct object_base *)obj_surface;
	src_surface.type = I965_SURFACE_TYPE_SURFACE;
	src_surface.flags = I965_SURFACE_FLAG_FRAME;

	status = i965_CreateSurfaces(ctx,
								 obj_surface->orig_width,
								 obj_surface->orig_height,
								 format,
								 1,
								 &encoder_context->input_yuv_surface);
	ASSERT_RET(status == VA_STATUS_SUCCESS, status);

	obj_surface = SURFACE(encoder_context->input_yuv_surface);
	encode_state->input_yuv_object = obj_surface;
	assert(obj_surface);
	i965_check_alloc_surface_bo(ctx, obj_surface, 1, fourcc, SUBSAMPLE_YUV420);

	dst_surface.base = (struct object_base *)obj_surface;
	dst_surface.type = I965_SURFACE_TYPE_SURFACE;
	dst_surface.flags = I965_SURFACE_FLAG_FRAME;

	status = i965_image_processing(ctx,
								   &src_surface,
								   &rect,
								   &dst_surface,
								   &rect);
	assert(status == VA_STATUS_SUCCESS);

	encoder_context->is_tmp_id = 1;

	return clear_border(obj_surface);
}


static VAStatus
intel_encoder_check_jpeg_yuv_surface(VADriverContextP ctx,
									 VAProfile profile,
									 struct encode_state *encode_state,
									 struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct i965_surface src_surface, dst_surface;
	struct object_surface *obj_surface;
	VAStatus status;
	VARectangle rect;
	int format = 0, fourcc = 0, subsample = 0;

	/* releae the temporary surface */
	if (encoder_context->is_tmp_id) {
		i965_DestroySurfaces(ctx, &encoder_context->input_yuv_surface, 1);
		encode_state->input_yuv_object = NULL;
	}

	encoder_context->is_tmp_id = 0;
	obj_surface = SURFACE(encode_state->current_render_target);
	assert(obj_surface && obj_surface->bo);

	if (!obj_surface || !obj_surface->bo)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	unsigned int tiling = 0, swizzle = 0;

	dri_bo_get_tiling(obj_surface->bo, &tiling, &swizzle);

	if (tiling == I915_TILING_Y) {
		if ((obj_surface->fourcc == VA_FOURCC_NV12)  || (obj_surface->fourcc == VA_FOURCC_UYVY) ||
			(obj_surface->fourcc == VA_FOURCC_YUY2)  || (obj_surface->fourcc == VA_FOURCC_Y800) ||
			(obj_surface->fourcc == VA_FOURCC_RGBA)  || (obj_surface->fourcc == VA_FOURCC_444P)) {
			encoder_context->input_yuv_surface = encode_state->current_render_target;
			encode_state->input_yuv_object = obj_surface;
			return VA_STATUS_SUCCESS;
		}
	}

	rect.x = 0;
	rect.y = 0;
	rect.width = obj_surface->orig_width;
	rect.height = obj_surface->orig_height;

	src_surface.base = (struct object_base *)obj_surface;
	src_surface.type = I965_SURFACE_TYPE_SURFACE;
	src_surface.flags = I965_SURFACE_FLAG_FRAME;

	switch (obj_surface->fourcc) {

	case VA_FOURCC_YUY2:
		fourcc = VA_FOURCC_YUY2;
		format = VA_RT_FORMAT_YUV422;
		subsample = SUBSAMPLE_YUV422H;
		break;

	case VA_FOURCC_UYVY:
		fourcc = VA_FOURCC_UYVY;
		format = VA_RT_FORMAT_YUV422;
		subsample = SUBSAMPLE_YUV422H;
		break;

	case VA_FOURCC_Y800:
		fourcc = VA_FOURCC_Y800;
		format = VA_RT_FORMAT_YUV400;
		subsample = SUBSAMPLE_YUV400;
		break;

	case VA_FOURCC_444P:
		fourcc = VA_FOURCC_444P;
		format = VA_RT_FORMAT_YUV444;
		subsample = SUBSAMPLE_YUV444;
		break;

	case VA_FOURCC_RGBA:
		fourcc = VA_FOURCC_RGBA;
		format = VA_RT_FORMAT_RGB32;
		subsample = SUBSAMPLE_RGBX;
		break;

	default: //All other scenarios will have NV12 format
		fourcc = VA_FOURCC_NV12;
		format = VA_RT_FORMAT_YUV420;
		subsample = SUBSAMPLE_YUV420;
		break;
	}

	status = i965_CreateSurfaces(ctx,
								 obj_surface->orig_width,
								 obj_surface->orig_height,
								 format,
								 1,
								 &encoder_context->input_yuv_surface);
	assert(status == VA_STATUS_SUCCESS);

	if (status != VA_STATUS_SUCCESS)
		return status;

	obj_surface = SURFACE(encoder_context->input_yuv_surface);
	encode_state->input_yuv_object = obj_surface;
	assert(obj_surface);
	i965_check_alloc_surface_bo(ctx, obj_surface, 1, fourcc, subsample);

	dst_surface.base = (struct object_base *)obj_surface;
	dst_surface.type = I965_SURFACE_TYPE_SURFACE;
	dst_surface.flags = I965_SURFACE_FLAG_FRAME;

	//The Y800 format is expected to be tiled.
	//Linear Y800 is a corner case and needs code in the i965_image_processing.
	if (obj_surface->fourcc != VA_FOURCC_Y800) {
		status = i965_image_processing(ctx,
									   &src_surface,
									   &rect,
									   &dst_surface,
									   &rect);
		assert(status == VA_STATUS_SUCCESS);
	}

	encoder_context->is_tmp_id = 1;

	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_brc_h264_sequence_parameter(VADriverContextP ctx,
												struct encode_state *encode_state,
												struct intel_encoder_context *encoder_context,
												unsigned int *seq_bits_per_second)
{
	VAEncSequenceParameterBufferH264 *seq_param = (VAEncSequenceParameterBufferH264 *)encode_state->seq_param_ext->buffer;
	struct intel_fraction framerate;
	unsigned short num_pframes_in_gop, num_bframes_in_gop;

	if (!encoder_context->is_new_sequence)
		return VA_STATUS_SUCCESS;

	assert(seq_param);

	if (!seq_param->num_units_in_tick || !seq_param->time_scale) {
		framerate = (struct intel_fraction) {
			30, 1
		};
	} else {
		// for the highest layer
		framerate = (struct intel_fraction) {
			seq_param->time_scale, 2 * seq_param->num_units_in_tick
		};
	}
	framerate = reduce_fraction(framerate);

	encoder_context->brc.num_iframes_in_gop = 1; // Always 1

	if (seq_param->intra_period == 0) { // E.g. IDRPP... / IDR(PBB)... (no IDR/I any more)
		if (seq_param->ip_period == 0)
			goto error;

		encoder_context->brc.gop_size = (framerate.num + framerate.den - 1) / framerate.den; // fake
		num_pframes_in_gop = (encoder_context->brc.gop_size +
							  seq_param->ip_period - 1) / seq_param->ip_period - 1;
	} else if (seq_param->intra_period == 1) { // E.g. IDRIII...
		encoder_context->brc.gop_size = 1;
		num_pframes_in_gop = 0;
	} else {
		if (seq_param->ip_period == 0)
			goto error;

		encoder_context->brc.gop_size = seq_param->intra_period;
		num_pframes_in_gop = (encoder_context->brc.gop_size +
							  seq_param->ip_period - 1) / seq_param->ip_period - 1;
	}

	num_bframes_in_gop = (encoder_context->brc.gop_size -
						  encoder_context->brc.num_iframes_in_gop - num_pframes_in_gop);

	if (num_pframes_in_gop != encoder_context->brc.num_pframes_in_gop ||
		num_bframes_in_gop != encoder_context->brc.num_bframes_in_gop ||
		framerate.num != encoder_context->brc.framerate[encoder_context->layer.num_layers - 1].num ||
		framerate.den != encoder_context->brc.framerate[encoder_context->layer.num_layers - 1].den) {
		encoder_context->brc.num_pframes_in_gop = num_pframes_in_gop;
		encoder_context->brc.num_bframes_in_gop = num_bframes_in_gop;
		encoder_context->brc.framerate[encoder_context->layer.num_layers - 1] = framerate;
		encoder_context->brc.need_reset = 1;
	}

	if (!encoder_context->brc.hrd_buffer_size ||
		!encoder_context->brc.hrd_initial_buffer_fullness) {
		encoder_context->brc.hrd_buffer_size = seq_param->bits_per_second << 1;
		encoder_context->brc.hrd_initial_buffer_fullness = seq_param->bits_per_second;
	}

	*seq_bits_per_second = seq_param->bits_per_second;

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_encoder_check_brc_vp8_sequence_parameter(VADriverContextP ctx,
											   struct encode_state *encode_state,
											   struct intel_encoder_context *encoder_context,
											   unsigned int *seq_bits_per_second)
{
	VAEncSequenceParameterBufferVP8 *seq_param = (VAEncSequenceParameterBufferVP8 *)encode_state->seq_param_ext->buffer;
	unsigned int num_pframes_in_gop;

	if (!encoder_context->is_new_sequence)
		return VA_STATUS_SUCCESS;

	assert(seq_param);

	encoder_context->brc.num_iframes_in_gop = 1;// Always 1
	encoder_context->brc.num_bframes_in_gop = 0;// No B frame

	if (seq_param->intra_period == 0) {         // E.g. IPPP... (only one I frame in the stream)
		encoder_context->brc.gop_size = 30;     // fake
	} else {
		encoder_context->brc.gop_size = seq_param->intra_period;
	}

	num_pframes_in_gop = encoder_context->brc.gop_size - 1;

	if (!encoder_context->brc.framerate[encoder_context->layer.num_layers - 1].num) {
		// for the highest layer
		encoder_context->brc.framerate[encoder_context->layer.num_layers - 1] = (struct intel_fraction) {
			30, 1
		};
		encoder_context->brc.need_reset = 1;
	}

	if (num_pframes_in_gop != encoder_context->brc.num_pframes_in_gop) {
		encoder_context->brc.num_pframes_in_gop = num_pframes_in_gop;
		encoder_context->brc.need_reset = 1;
	}

	if (!encoder_context->brc.hrd_buffer_size ||
		!encoder_context->brc.hrd_initial_buffer_fullness) {
		encoder_context->brc.hrd_buffer_size = seq_param->bits_per_second << 1;
		encoder_context->brc.hrd_initial_buffer_fullness = seq_param->bits_per_second;
		encoder_context->brc.need_reset = 1;
	}

	*seq_bits_per_second = seq_param->bits_per_second;

	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_brc_hevc_sequence_parameter(VADriverContextP ctx,
												struct encode_state *encode_state,
												struct intel_encoder_context *encoder_context,
												unsigned int *seq_bits_per_second)
{
	VAEncSequenceParameterBufferHEVC *seq_param = (VAEncSequenceParameterBufferHEVC*)encode_state->seq_param_ext->buffer;
	struct intel_fraction framerate;
	unsigned int gop_size, num_iframes_in_gop, num_pframes_in_gop, num_bframes_in_gop;

	if (!encoder_context->is_new_sequence)
		return VA_STATUS_SUCCESS;
	if (!seq_param)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	if (!seq_param->vui_time_scale || !seq_param->vui_num_units_in_tick)
		framerate = (struct intel_fraction) {
		30, 1
	};
	else
		framerate = (struct intel_fraction) {
		seq_param->vui_time_scale, seq_param->vui_num_units_in_tick
	};
	framerate = reduce_fraction(framerate);

	num_iframes_in_gop = 1;
	if (seq_param->intra_period == 0) {
		gop_size = -1;
		num_pframes_in_gop = -1;
	} else if (seq_param->intra_period == 1) {
		gop_size = 1;
		num_pframes_in_gop = 0;
	} else {
		gop_size = seq_param->intra_period;
		num_pframes_in_gop = (seq_param->intra_period + seq_param->ip_period - 1) / seq_param->ip_period - 1;
	}
	num_bframes_in_gop = gop_size - num_iframes_in_gop - num_pframes_in_gop;

	if (encoder_context->brc.framerate[0].num != framerate.num ||
		encoder_context->brc.framerate[0].den != framerate.den) {
		encoder_context->brc.framerate[0] = framerate;
		encoder_context->brc.need_reset = 1;
	}

	if (encoder_context->brc.gop_size != gop_size ||
		encoder_context->brc.num_iframes_in_gop != num_iframes_in_gop ||
		encoder_context->brc.num_pframes_in_gop != num_pframes_in_gop ||
		encoder_context->brc.num_bframes_in_gop != num_bframes_in_gop) {
		encoder_context->brc.gop_size = gop_size;
		encoder_context->brc.num_iframes_in_gop = num_iframes_in_gop;
		encoder_context->brc.num_pframes_in_gop = num_pframes_in_gop;
		encoder_context->brc.num_bframes_in_gop = num_bframes_in_gop;
		encoder_context->brc.need_reset = 1;
	}

	*seq_bits_per_second = seq_param->bits_per_second;

	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_brc_vp9_sequence_parameter(VADriverContextP ctx,
											   struct encode_state *encode_state,
											   struct intel_encoder_context *encoder_context,
											   unsigned int *seq_bits_per_second)
{
	VAEncSequenceParameterBufferVP9 *seq_param = (VAEncSequenceParameterBufferVP9*)encode_state->seq_param_ext->buffer;
	unsigned int gop_size;

	if (!encoder_context->is_new_sequence)
		return VA_STATUS_SUCCESS;
	if (!seq_param)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	if (seq_param->intra_period == 0)
		gop_size = -1; // Dummy value (infinity).
	else
		gop_size = seq_param->intra_period;

	if (encoder_context->brc.gop_size != gop_size) {
		encoder_context->brc.gop_size = gop_size;
		encoder_context->brc.need_reset = 1;
	}

	*seq_bits_per_second = seq_param->bits_per_second;

	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_brc_sequence_parameter(VADriverContextP ctx,
										   struct encode_state *encode_state,
										   struct intel_encoder_context *encoder_context,
										   unsigned int *seq_bits_per_second)
{
	*seq_bits_per_second = 0;

	switch (encoder_context->codec) {
	case CODEC_H264:
	case CODEC_H264_MVC:
		return intel_encoder_check_brc_h264_sequence_parameter(ctx, encode_state, encoder_context, seq_bits_per_second);

	case CODEC_VP8:
		return intel_encoder_check_brc_vp8_sequence_parameter(ctx, encode_state, encoder_context, seq_bits_per_second);

	case CODEC_HEVC:
		return intel_encoder_check_brc_hevc_sequence_parameter(ctx, encode_state, encoder_context, seq_bits_per_second);

	case CODEC_VP9:
		return intel_encoder_check_brc_vp9_sequence_parameter(ctx, encode_state, encoder_context, seq_bits_per_second);

	default:
		// TODO: other codecs
		return VA_STATUS_SUCCESS;
	}
}

static void
intel_encoder_check_rate_control_parameter(VADriverContextP ctx,
										   struct intel_encoder_context *encoder_context,
										   VAEncMiscParameterRateControl *misc,
										   int *hl_bitrate_updated)
{
	int temporal_id = 0;

	if (encoder_context->layer.num_layers >= 2)
		temporal_id = misc->rc_flags.bits.temporal_id;

	if (temporal_id >= encoder_context->layer.num_layers)
		return;

	if (misc->rc_flags.bits.reset)
		encoder_context->brc.need_reset = 1;

	if (encoder_context->brc.bits_per_second[temporal_id] != misc->bits_per_second) {
		encoder_context->brc.bits_per_second[temporal_id] = misc->bits_per_second;
		encoder_context->brc.need_reset = 1;
	}

	if (encoder_context->brc.mb_rate_control[temporal_id] != misc->rc_flags.bits.mb_rate_control) {
		encoder_context->brc.mb_rate_control[temporal_id] = misc->rc_flags.bits.mb_rate_control;
		encoder_context->brc.need_reset = 1;
	}

	if (encoder_context->brc.target_percentage[temporal_id] != misc->target_percentage) {
		encoder_context->brc.target_percentage[temporal_id] = misc->target_percentage;
		encoder_context->brc.need_reset = 1;
	}

	if (encoder_context->brc.window_size != misc->window_size ||
		encoder_context->brc.initial_qp  != misc->initial_qp ||
		encoder_context->brc.min_qp      != misc->min_qp) {
		encoder_context->brc.window_size = misc->window_size;
		encoder_context->brc.initial_qp  = misc->initial_qp;
		encoder_context->brc.min_qp      = misc->min_qp;
		encoder_context->brc.need_reset = 1;
	}

	if (temporal_id == encoder_context->layer.num_layers - 1)
		*hl_bitrate_updated = 1;
}

static void
intel_encoder_check_hrd_parameter(VADriverContextP ctx,
								  struct intel_encoder_context *encoder_context,
								  VAEncMiscParameterHRD *misc)
{
	if (encoder_context->brc.hrd_buffer_size != misc->buffer_size ||
		encoder_context->brc.hrd_initial_buffer_fullness != misc->initial_buffer_fullness) {
		encoder_context->brc.hrd_buffer_size = misc->buffer_size;
		encoder_context->brc.hrd_initial_buffer_fullness = misc->initial_buffer_fullness;
		encoder_context->brc.need_reset = 1;
	}
}

static void
intel_encoder_check_framerate_parameter(VADriverContextP ctx,
										struct intel_encoder_context *encoder_context,
										VAEncMiscParameterFrameRate *misc)
{
	struct intel_fraction framerate;
	int temporal_id = 0;

	if (encoder_context->layer.num_layers >= 2)
		temporal_id = misc->framerate_flags.bits.temporal_id;

	if (temporal_id >= encoder_context->layer.num_layers)
		return;

	if (misc->framerate & 0xffff0000)
		framerate = (struct intel_fraction) {
		misc->framerate & 0xffff, misc->framerate >> 16 & 0xffff
	};
	else
		framerate = (struct intel_fraction) {
		misc->framerate, 1
	};
	framerate = reduce_fraction(framerate);

	if (encoder_context->brc.framerate[temporal_id].num != framerate.num ||
		encoder_context->brc.framerate[temporal_id].den != framerate.den) {
		encoder_context->brc.framerate[temporal_id] = framerate;
		encoder_context->brc.need_reset = 1;
	}
}

static void
intel_encoder_check_roi_parameter(VADriverContextP ctx,
								  struct intel_encoder_context *encoder_context,
								  VAEncMiscParameterBufferROI *misc)
{
	int i = 0;

	encoder_context->brc.num_roi = MIN(misc->num_roi, I965_MAX_NUM_ROI_REGIONS);
	encoder_context->brc.roi_max_delta_qp = misc->max_delta_qp;
	encoder_context->brc.roi_min_delta_qp = misc->min_delta_qp;
	encoder_context->brc.roi_value_is_qp_delta = 0;

	if (encoder_context->rate_control_mode != VA_RC_CQP)
		encoder_context->brc.roi_value_is_qp_delta = misc->roi_flags.bits.roi_value_is_qp_delta;

	for (i = 0; i <  encoder_context->brc.num_roi; i++) {
		encoder_context->brc.roi[i].left = misc->roi->roi_rectangle.x;
		encoder_context->brc.roi[i].right = encoder_context->brc.roi[i].left + misc->roi->roi_rectangle.width;
		encoder_context->brc.roi[i].top = misc->roi->roi_rectangle.y;
		encoder_context->brc.roi[i].bottom = encoder_context->brc.roi[i].top + misc->roi->roi_rectangle.height;
		encoder_context->brc.roi[i].value = misc->roi->roi_value;
	}
}

static VAStatus
intel_encoder_check_brc_parameter(VADriverContextP ctx,
								  struct encode_state *encode_state,
								  struct intel_encoder_context *encoder_context)
{
	VAStatus ret;
	VAEncMiscParameterBuffer *misc_param;
	int i, j;
	int hl_bitrate_updated = 0; // Indicate whether the bitrate for the highest level is changed in misc parameters
	unsigned int seq_bits_per_second = 0;

	if (encoder_context->rate_control_mode & (VA_RC_CBR | VA_RC_VBR)) {
		ret = intel_encoder_check_brc_sequence_parameter(ctx, encode_state, encoder_context, &seq_bits_per_second);

		if (ret)
			return ret;
	}

	for (i = 0; i < ARRAY_ELEMS(encode_state->misc_param); i++) {
		for (j = 0; j < ARRAY_ELEMS(encode_state->misc_param[0]); j++) {
			if (!encode_state->misc_param[i][j] || !encode_state->misc_param[i][j]->buffer)
				continue;

			misc_param = (VAEncMiscParameterBuffer *)encode_state->misc_param[i][j]->buffer;

			if (!(encoder_context->rate_control_mode & (VA_RC_CBR | VA_RC_VBR))) {
				if (misc_param->type != VAEncMiscParameterTypeROI)
					continue;
			}

			switch (misc_param->type) {
			case VAEncMiscParameterTypeFrameRate:
				intel_encoder_check_framerate_parameter(ctx,
														encoder_context,
														(VAEncMiscParameterFrameRate *)misc_param->data);
				break;

			case VAEncMiscParameterTypeRateControl:
				intel_encoder_check_rate_control_parameter(ctx,
														   encoder_context,
														   (VAEncMiscParameterRateControl *)misc_param->data,
														   &hl_bitrate_updated);
				break;

			case VAEncMiscParameterTypeHRD:
				intel_encoder_check_hrd_parameter(ctx,
												  encoder_context,
												  (VAEncMiscParameterHRD *)misc_param->data);
				break;

			case VAEncMiscParameterTypeROI:
				intel_encoder_check_roi_parameter(ctx,
												  encoder_context,
												  (VAEncMiscParameterBufferROI *)misc_param->data);
				break;

			default:
				break;
			}
		}
	}

	if (!hl_bitrate_updated && seq_bits_per_second &&
		encoder_context->brc.bits_per_second[encoder_context->layer.num_layers - 1] != seq_bits_per_second) {

		encoder_context->brc.bits_per_second[encoder_context->layer.num_layers - 1] = seq_bits_per_second;
		encoder_context->brc.need_reset = 1;

	}

	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_temporal_layer_structure(VADriverContextP ctx,
											 struct encode_state *encode_state,
											 struct intel_encoder_context *encoder_context)
{
	VAEncMiscParameterBuffer* misc_param;
	VAEncMiscParameterTemporalLayerStructure *tls_paramter;
	unsigned int rate_control_mode = encoder_context->rate_control_mode;
	int i;

	if (!encoder_context->is_new_sequence) {
		if (encoder_context->layer.num_layers > 1)
			encoder_context->layer.curr_frame_layer_id = encoder_context->layer.frame_layer_ids[(encoder_context->num_frames_in_sequence - 1) % encoder_context->layer.size_frame_layer_ids];
		else
			encoder_context->layer.curr_frame_layer_id = 0;

		return VA_STATUS_SUCCESS;
	}

	if (!(rate_control_mode & (VA_RC_CBR | VA_RC_VBR)))
		return VA_STATUS_SUCCESS;

	if (!encode_state->misc_param[VAEncMiscParameterTypeTemporalLayerStructure][0] ||
		!encode_state->misc_param[VAEncMiscParameterTypeTemporalLayerStructure][0]->buffer)
		return VA_STATUS_SUCCESS;

	misc_param = (VAEncMiscParameterBuffer*)encode_state->misc_param[VAEncMiscParameterTypeTemporalLayerStructure][0]->buffer;
	tls_paramter = (VAEncMiscParameterTemporalLayerStructure *)misc_param->data;

	if (tls_paramter->number_of_layers <= 1)
		return VA_STATUS_SUCCESS;

	if (tls_paramter->number_of_layers > MAX_TEMPORAL_LAYERS)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	if (tls_paramter->periodicity > 32 || tls_paramter->periodicity <= 1)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	for (i = 0; i < tls_paramter->number_of_layers; i++) {
		if (!encode_state->misc_param[VAEncMiscParameterTypeRateControl][i] ||
			!encode_state->misc_param[VAEncMiscParameterTypeRateControl][i]->buffer ||
			!encode_state->misc_param[VAEncMiscParameterTypeFrameRate][i] ||
			!encode_state->misc_param[VAEncMiscParameterTypeFrameRate][i]->buffer) {

			return VA_STATUS_ERROR_INVALID_PARAMETER;
		}
	}

	encoder_context->layer.size_frame_layer_ids = tls_paramter->periodicity;
	encoder_context->layer.num_layers = tls_paramter->number_of_layers;

	for (i = 0; i < encoder_context->layer.size_frame_layer_ids; i++) {
		if (tls_paramter->layer_id[i] >= tls_paramter->number_of_layers)
			return VA_STATUS_ERROR_INVALID_PARAMETER;

		encoder_context->layer.frame_layer_ids[i] = tls_paramter->layer_id[i];
	}

	if (encoder_context->is_new_sequence)
		encoder_context->layer.curr_frame_layer_id = 0;
	else
		encoder_context->layer.curr_frame_layer_id = encoder_context->layer.frame_layer_ids[(encoder_context->num_frames_in_sequence - 1) % encoder_context->layer.size_frame_layer_ids];

	return VA_STATUS_SUCCESS;
}

static VAStatus
intel_encoder_check_misc_parameter(VADriverContextP ctx,
								   VAProfile profile,
								   struct encode_state *encode_state,
								   struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	VAStatus ret = VA_STATUS_SUCCESS;
	int min_width_height = I965_MIN_CODEC_ENC_RESOLUTION_WIDTH_HEIGHT;

	if (encoder_context->frame_width_in_pixel > 0 &&
		encoder_context->frame_height_in_pixel > 0) {
		if (profile == VAProfileJPEGBaseline)
			min_width_height = 1;
		if (encoder_context->frame_width_in_pixel < min_width_height ||
			encoder_context->frame_height_in_pixel < min_width_height)
			return VA_STATUS_ERROR_INVALID_PARAMETER;
	}

	if (encode_state->misc_param[VAEncMiscParameterTypeQualityLevel][0] &&
		encode_state->misc_param[VAEncMiscParameterTypeQualityLevel][0]->buffer) {
		VAEncMiscParameterBuffer* pMiscParam = (VAEncMiscParameterBuffer*)encode_state->misc_param[VAEncMiscParameterTypeQualityLevel][0]->buffer;
		VAEncMiscParameterBufferQualityLevel* param_quality_level = (VAEncMiscParameterBufferQualityLevel*)pMiscParam->data;
		encoder_context->quality_level = param_quality_level->quality_level;

		if (encoder_context->quality_level == 0) {
			switch (profile) {
			case VAProfileH264ConstrainedBaseline:
			case VAProfileH264Main:
			case VAProfileH264High:
			case VAProfileH264MultiviewHigh:
			case VAProfileH264StereoHigh:
				if (IS_GEN9(i965->intel.device_info) ||
					IS_GEN10(i965->intel.device_info))
					encoder_context->quality_level = ENCODER_DEFAULT_QUALITY_AVC;
				break;

			case VAProfileHEVCMain:
			case VAProfileHEVCMain10:
				encoder_context->quality_level = ENCODER_DEFAULT_QUALITY_HEVC;
				break;

			default:
				encoder_context->quality_level = ENCODER_DEFAULT_QUALITY;
				break;
			}
		} else if (encoder_context->quality_level > encoder_context->quality_range) {
			i965_log_info(ctx, "VAEncMiscParameterBufferQualityLevel.quality_level (%d) out of range (max %d).\n",
						  encoder_context->quality_level, encoder_context->quality_range);
			ret = VA_STATUS_ERROR_INVALID_PARAMETER;
			goto out;
		}
	}

	ret = intel_encoder_check_temporal_layer_structure(ctx, encode_state, encoder_context);

	if (ret)
		goto out;

	ret = intel_encoder_check_brc_parameter(ctx, encode_state, encoder_context);

out:
	return ret;
}

static VAStatus
intel_encoder_check_avc_parameter(VADriverContextP ctx,
								  struct encode_state *encode_state,
								  struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct object_surface *obj_surface;
	struct object_buffer *obj_buffer;
	VAEncPictureParameterBufferH264 *pic_param = (VAEncPictureParameterBufferH264 *)encode_state->pic_param_ext->buffer;
	VAEncSequenceParameterBufferH264 *seq_param = (VAEncSequenceParameterBufferH264 *)encode_state->seq_param_ext->buffer;
	int i;

	if (!seq_param)
		goto error;

	if (seq_param->level_idc != encoder_context->codec_level &&
		!i965_avc_level_is_valid(seq_param->level_idc)) {
		i965_log_info(ctx, "VAEncSequenceParameterBufferH264.level_idc (%d) does not appear to be valid.\n",
					  seq_param->level_idc);
		encoder_context->codec_level = seq_param->level_idc;
		// Only print this the first time we see it, and continue anyway - this could be a correct future
		// value or an unknown extension.
	}

	if (pic_param->CurrPic.flags & VA_PICTURE_H264_INVALID) {
		i965_log_info(ctx, "VAEncPictureParameterBufferH264.CurrPic.flags (%#x) is invalid.\n",
					  pic_param->CurrPic.flags);
		goto error;
	}

	obj_surface = SURFACE(pic_param->CurrPic.picture_id);
	if (!obj_surface) {
		i965_log_info(ctx, "VAEncPictureParameterBufferH264.CurrPic.picture_id (%#x) is not a valid surface.\n",
					  pic_param->CurrPic.picture_id);
		goto error;
	}

	encode_state->reconstructed_object = obj_surface;
	obj_buffer = BUFFER(pic_param->coded_buf);
	if (!obj_buffer || !obj_buffer->buffer_store || !obj_buffer->buffer_store->bo) {
		i965_log_info(ctx, "VAEncPictureParameterBufferH264.coded_buf (%#x) is not a valid buffer.\n",
					  pic_param->coded_buf);
		goto error;
	}

	if (encode_state->num_slice_params_ext > encoder_context->max_slice_or_seg_num) {
		i965_log_info(ctx, "Too many slices in picture submission: %d, max supported is %d.\n",
					  encode_state->num_slice_params_ext, encoder_context->max_slice_or_seg_num);
		goto error;
	}

	encode_state->coded_buf_object = obj_buffer;

	for (i = 0; i < 16; i++) {
		if (pic_param->ReferenceFrames[i].flags & VA_PICTURE_H264_INVALID ||
			pic_param->ReferenceFrames[i].picture_id == VA_INVALID_SURFACE)
			break;
		else {
			obj_surface = SURFACE(pic_param->ReferenceFrames[i].picture_id);
			if (!obj_surface || !obj_surface->bo) {
				i965_log_info(ctx, "VAEncPictureParameterBufferH264.ReferenceFrames[%d].picture_id (%#x)"
							  " is not a valid surface.\n", i, pic_param->ReferenceFrames[i].picture_id);
				goto error;
			}
			encode_state->reference_objects[i] = obj_surface;
		}
	}

	for (; i < 16; i++)
		encode_state->reference_objects[i] = NULL;

	/*
	 * A sequence consists of an IDR unit, followed by zero or more non-IDR unit, but not including any
	 * subsequent IDR unit, so idr_pic_flag can indicate the current frame is the start of a new
	 * sequnce
	 */
	encoder_context->is_new_sequence = pic_param->pic_fields.bits.idr_pic_flag;

	if (encoder_context->is_new_sequence) {
		encoder_context->num_frames_in_sequence = 0;
		encoder_context->frame_width_in_pixel = seq_param->picture_width_in_mbs * 16;
		encoder_context->frame_height_in_pixel = seq_param->picture_height_in_mbs * 16;
	}

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_pre_encoder_check_avc_parameter(VADriverContextP ctx,
									  struct encode_state *encode_state,
									  struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct object_surface *obj_surface = NULL;
	VAStatsStatisticsParameterH264 *stat_param_h264 = NULL;
	VAStatsStatisticsParameter*stat_param = NULL;

	if (!encode_state->stat_param_ext)
		goto error;
	stat_param_h264 =
		(VAStatsStatisticsParameterH264 *) encode_state->stat_param_ext->buffer;
	stat_param = (VAStatsStatisticsParameter *)(&stat_param_h264->stats_params);

	if (stat_param->input.flags == VA_PICTURE_STATS_INVALID)
		goto error;

	obj_surface = SURFACE(encoder_context->input_yuv_surface);
	if (!obj_surface)
		goto error;

#if 0
	/* FeiPreEncFixme: Since the driver is doing internal CSC for non NV12
	  input surfaces, this check may fail here */
	/* Make sure the same input yuv has been provided in vaBeginPicture()
	 * and VAStatsStatisticsParameter */
	if (obj_surface != SURFACE(stat_param->input.picture_id))
		goto error;
#endif

	/* There is no reconstructed object in preenc. Here we just assigning
	 * the input yuv object to reconstructed object pointer inorder
	 * to use the same encode code path later on */
	encode_state->reconstructed_object = obj_surface;

	encoder_context->frame_width_in_pixel = obj_surface->orig_width;
	encoder_context->frame_height_in_pixel = obj_surface->orig_height;

	/* PreEnc only supports maxium of 1 past and 1 future reference */
	if (stat_param->num_past_references > 1 || stat_param->num_future_references > 1)
		goto error;

	return VA_STATUS_SUCCESS;
error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_encoder_check_mpeg2_parameter(VADriverContextP ctx,
									struct encode_state *encode_state,
									struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	VAEncPictureParameterBufferMPEG2 *pic_param = (VAEncPictureParameterBufferMPEG2 *)encode_state->pic_param_ext->buffer;
	VAEncSequenceParameterBufferMPEG2 *seq_param = NULL;
	struct object_surface *obj_surface;
	struct object_buffer *obj_buffer;
	int i = 0;

	if (encode_state->seq_param_ext &&
		encode_state->seq_param_ext->buffer)
		seq_param = (VAEncSequenceParameterBufferMPEG2 *)encode_state->seq_param_ext->buffer;

	obj_surface = SURFACE(pic_param->reconstructed_picture);
	assert(obj_surface); /* It is possible the store buffer isn't allocated yet */

	if (!obj_surface)
		goto error;

	encode_state->reconstructed_object = obj_surface;
	obj_buffer = BUFFER(pic_param->coded_buf);
	assert(obj_buffer && obj_buffer->buffer_store && obj_buffer->buffer_store->bo);

	if (!obj_buffer || !obj_buffer->buffer_store || !obj_buffer->buffer_store->bo)
		goto error;

	encode_state->coded_buf_object = obj_buffer;

	if (pic_param->picture_type == VAEncPictureTypeIntra) {
	} else if (pic_param->picture_type == VAEncPictureTypePredictive) {
		assert(pic_param->forward_reference_picture != VA_INVALID_SURFACE);
		obj_surface = SURFACE(pic_param->forward_reference_picture);
		assert(obj_surface && obj_surface->bo);

		if (!obj_surface || !obj_surface->bo)
			goto error;

		encode_state->reference_objects[i++] = obj_surface;
	} else if (pic_param->picture_type == VAEncPictureTypeBidirectional) {
		assert(pic_param->forward_reference_picture != VA_INVALID_SURFACE);
		obj_surface = SURFACE(pic_param->forward_reference_picture);
		assert(obj_surface && obj_surface->bo);

		if (!obj_surface || !obj_surface->bo)
			goto error;

		encode_state->reference_objects[i++] = obj_surface;

		assert(pic_param->backward_reference_picture != VA_INVALID_SURFACE);
		obj_surface = SURFACE(pic_param->backward_reference_picture);
		assert(obj_surface && obj_surface->bo);

		if (!obj_surface || !obj_surface->bo)
			goto error;

		encode_state->reference_objects[i++] = obj_surface;
	} else
		goto error;

	for (; i < 16; i++)
		encode_state->reference_objects[i] = NULL;

	if (seq_param) {
		encoder_context->frame_width_in_pixel = seq_param->picture_width;
		encoder_context->frame_height_in_pixel = seq_param->picture_height;
	}

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_encoder_check_jpeg_parameter(VADriverContextP ctx,
								   struct encode_state *encode_state,
								   struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct object_buffer *obj_buffer;
	VAEncPictureParameterBufferJPEG *pic_param = (VAEncPictureParameterBufferJPEG *)encode_state->pic_param_ext->buffer;


	assert(!(pic_param->pic_flags.bits.profile)); //Baseline profile is 0.

	obj_buffer = BUFFER(pic_param->coded_buf);
	assert(obj_buffer && obj_buffer->buffer_store && obj_buffer->buffer_store->bo);

	if (!obj_buffer || !obj_buffer->buffer_store || !obj_buffer->buffer_store->bo)
		goto error;

	encode_state->coded_buf_object = obj_buffer;

	encoder_context->frame_width_in_pixel = pic_param->picture_width;
	encoder_context->frame_height_in_pixel = pic_param->picture_height;

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_encoder_check_vp8_parameter(VADriverContextP ctx,
								  struct encode_state *encode_state,
								  struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	VAEncPictureParameterBufferVP8 *pic_param = (VAEncPictureParameterBufferVP8 *)encode_state->pic_param_ext->buffer;
	VAEncSequenceParameterBufferVP8 *seq_param = (VAEncSequenceParameterBufferVP8 *)encode_state->seq_param_ext->buffer;
	struct object_surface *obj_surface;
	struct object_buffer *obj_buffer;
	int i = 0;
	int is_key_frame = !pic_param->pic_flags.bits.frame_type;

	obj_surface = SURFACE(pic_param->reconstructed_frame);

	if (!obj_surface)
		goto error;

	encode_state->reconstructed_object = obj_surface;
	obj_buffer = BUFFER(pic_param->coded_buf);

	if (!obj_buffer || !obj_buffer->buffer_store || !obj_buffer->buffer_store->bo)
		goto error;

	encode_state->coded_buf_object = obj_buffer;

	if (!is_key_frame) {

		if (!pic_param->ref_flags.bits.no_ref_last) {
			obj_surface = SURFACE(pic_param->ref_last_frame);

			if (!obj_surface || !obj_surface->bo)
				goto error;

			encode_state->reference_objects[i++] = obj_surface;
		} else {
			encode_state->reference_objects[i++] = NULL;
		}

		if (!pic_param->ref_flags.bits.no_ref_gf) {
			obj_surface = SURFACE(pic_param->ref_gf_frame);

			if (!obj_surface || !obj_surface->bo)
				goto error;

			encode_state->reference_objects[i++] = obj_surface;
		} else {
			encode_state->reference_objects[i++] = NULL;
		}

		if (!pic_param->ref_flags.bits.no_ref_arf) {
			obj_surface = SURFACE(pic_param->ref_arf_frame);

			if (!obj_surface || !obj_surface->bo)
				goto error;

			encode_state->reference_objects[i++] = obj_surface;
		} else {
			encode_state->reference_objects[i++] = NULL;
		}
	}

	for (; i < 16; i++)
		encode_state->reference_objects[i] = NULL;

	encoder_context->is_new_sequence = (is_key_frame && seq_param);

	if (encoder_context->is_new_sequence) {
		encoder_context->num_frames_in_sequence = 0;
		encoder_context->frame_width_in_pixel = seq_param->frame_width;
		encoder_context->frame_height_in_pixel = seq_param->frame_height;
	}

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_encoder_check_hevc_parameter(VADriverContextP ctx,
								   struct encode_state *encode_state,
								   struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct object_surface *obj_surface;
	struct object_buffer *obj_buffer;
	VAEncPictureParameterBufferHEVC *pic_param = (VAEncPictureParameterBufferHEVC *)encode_state->pic_param_ext->buffer;
	VAEncSliceParameterBufferHEVC *slice_param;
	VAEncSequenceParameterBufferHEVC *seq_param;
	int i;

	seq_param = NULL;

	if (encode_state->seq_param_ext &&
		encode_state->seq_param_ext->buffer)
		seq_param = (VAEncSequenceParameterBufferHEVC *)(encode_state->seq_param_ext->buffer);

	if (pic_param->decoded_curr_pic.flags & VA_PICTURE_HEVC_INVALID) {
		i965_log_info(ctx, "VAEncPictureParameterBufferHEVC.decoded_curr_pic.flags (%#x) is invalid.\n",
					  pic_param->decoded_curr_pic.flags);
		goto error;
	}

	obj_surface = SURFACE(pic_param->decoded_curr_pic.picture_id);
	if (!obj_surface) {
		i965_log_info(ctx, "VAEncPictureParameterBufferHEVC.decoded_curr_pic.picture_id (%#x) is not a valid surface.\n",
					  pic_param->decoded_curr_pic.picture_id);
		goto error;
	}

	encode_state->reconstructed_object = obj_surface;
	obj_buffer = BUFFER(pic_param->coded_buf);
	if (!obj_buffer || !obj_buffer->buffer_store || !obj_buffer->buffer_store->bo) {
		i965_log_info(ctx, "VAEncPictureParameterBufferHEVC.coded_buf (%#x) is not a valid buffer.\n",
					  pic_param->coded_buf);
		goto error;
	}

	if (encode_state->num_slice_params_ext > encoder_context->max_slice_or_seg_num) {
		i965_log_info(ctx, "Too many slices in picture submission: %d, max supported is %d.\n",
					  encode_state->num_slice_params_ext, encoder_context->max_slice_or_seg_num);
		goto error;
	}

	encode_state->coded_buf_object = obj_buffer;

	for (i = 0; i < 15; i++) {
		if (pic_param->reference_frames[i].flags & VA_PICTURE_HEVC_INVALID ||
			pic_param->reference_frames[i].picture_id == VA_INVALID_SURFACE)
			break;
		else {
			obj_surface = SURFACE(pic_param->reference_frames[i].picture_id);
			if (!obj_surface || !obj_surface->bo) {
				i965_log_info(ctx, "VAEncPictureParameterBufferHEVC.reference_frames[%d].picture_id (%#x)"
							  " is not a valid surface.\n", i, pic_param->reference_frames[i].picture_id);
				goto error;
			}
			encode_state->reference_objects[i] = obj_surface;
		}
	}

	for (; i < 15; i++)
		encode_state->reference_objects[i] = NULL;

	for (i = 0; i < encode_state->num_slice_params_ext; i++) {
		slice_param = (VAEncSliceParameterBufferHEVC *)encode_state->slice_params_ext[i]->buffer;

		if (slice_param->slice_type != HEVC_SLICE_I &&
			slice_param->slice_type != HEVC_SLICE_P &&
			slice_param->slice_type != HEVC_SLICE_B)
			goto error;

		/* TODO: add more check here */
	}

	encoder_context->is_new_sequence = (pic_param->pic_fields.bits.idr_pic_flag && seq_param);

	if (encoder_context->is_new_sequence) {
		encoder_context->num_frames_in_sequence = 0;
		encoder_context->frame_width_in_pixel = seq_param->pic_width_in_luma_samples;
		encoder_context->frame_height_in_pixel = seq_param->pic_height_in_luma_samples;
	}

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}

static VAStatus
intel_encoder_check_vp9_parameter(VADriverContextP ctx,
								  struct encode_state *encode_state,
								  struct intel_encoder_context *encoder_context)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	VAEncPictureParameterBufferVP9 *pic_param;
	VAEncSequenceParameterBufferVP9 *seq_param;
	struct object_surface *obj_surface;
	struct object_buffer *obj_buffer;
	int i = 0;
	int is_key_frame = 0;
	int index;

	if (encode_state->pic_param_ext == NULL ||
		encode_state->pic_param_ext->buffer == NULL)
		return VA_STATUS_ERROR_INVALID_PARAMETER;

	seq_param = NULL;
	if (encode_state->seq_param_ext &&
		encode_state->seq_param_ext->buffer)
		seq_param = (VAEncSequenceParameterBufferVP9 *)(encode_state->seq_param_ext->buffer);

	pic_param = (VAEncPictureParameterBufferVP9 *)encode_state->pic_param_ext->buffer;

	obj_surface = SURFACE(pic_param->reconstructed_frame);

	if (!obj_surface)
		goto error;

	encode_state->reconstructed_object = obj_surface;
	obj_buffer = BUFFER(pic_param->coded_buf);

	if (!obj_buffer || !obj_buffer->buffer_store || !obj_buffer->buffer_store->bo)
		goto error;

	encode_state->coded_buf_object = obj_buffer;

	is_key_frame = !pic_param->pic_flags.bits.frame_type;
	if (!is_key_frame && !pic_param->pic_flags.bits.intra_only) {
		/* slot 0 is for last reference frame */
		index = pic_param->ref_flags.bits.ref_last_idx;
		obj_surface = SURFACE(pic_param->reference_frames[index]);
		if (obj_surface && obj_surface->bo)
			encode_state->reference_objects[i++] = obj_surface;
		else
			encode_state->reference_objects[i++] = NULL;

		/* slot 1 is for golden reference frame */
		index = pic_param->ref_flags.bits.ref_gf_idx;
		obj_surface = SURFACE(pic_param->reference_frames[index]);
		if (obj_surface && obj_surface->bo)
			encode_state->reference_objects[i++] = obj_surface;
		else
			encode_state->reference_objects[i++] = NULL;

		/* slot 2 is alt reference frame */
		index = pic_param->ref_flags.bits.ref_arf_idx;
		obj_surface = SURFACE(pic_param->reference_frames[index]);
		if (obj_surface && obj_surface->bo)
			encode_state->reference_objects[i++] = obj_surface;
		else
			encode_state->reference_objects[i++] = NULL;
	}

	for (; i < 16; i++)
		encode_state->reference_objects[i] = NULL;

	encoder_context->is_new_sequence = (is_key_frame && seq_param);

	encoder_context->frame_width_in_pixel = pic_param->frame_width_src;
	encoder_context->frame_height_in_pixel = pic_param->frame_height_src;

	return VA_STATUS_SUCCESS;

error:
	return VA_STATUS_ERROR_INVALID_PARAMETER;
}


static VAStatus
intel_encoder_sanity_check_input(VADriverContextP ctx,
								 VAProfile profile,
								 struct encode_state *encode_state,
								 struct intel_encoder_context *encoder_context)
{
	VAStatus vaStatus;

	switch (profile) {
	case VAProfileH264ConstrainedBaseline:
	case VAProfileH264Main:
	case VAProfileH264High:
	case VAProfileH264MultiviewHigh:
	case VAProfileH264StereoHigh: {
		if (!encoder_context->preenc_enabled) {
			vaStatus = intel_encoder_check_avc_parameter(ctx, encode_state, encoder_context);
			if (vaStatus != VA_STATUS_SUCCESS)
				goto out;
			vaStatus = intel_encoder_check_yuv_surface(ctx, profile, encode_state, encoder_context);
			break;
		} else {
			vaStatus = intel_encoder_check_yuv_surface(ctx, profile, encode_state, encoder_context);
			if (vaStatus != VA_STATUS_SUCCESS)
				goto out;

			vaStatus = intel_pre_encoder_check_avc_parameter(ctx, encode_state, encoder_context);
			goto out;
		}
	}

	case VAProfileMPEG2Simple:
	case VAProfileMPEG2Main: {
		vaStatus = intel_encoder_check_mpeg2_parameter(ctx, encode_state, encoder_context);
		if (vaStatus != VA_STATUS_SUCCESS)
			goto out;
		vaStatus = intel_encoder_check_yuv_surface(ctx, profile, encode_state, encoder_context);
		break;
	}

	case VAProfileJPEGBaseline:  {
		vaStatus = intel_encoder_check_jpeg_parameter(ctx, encode_state, encoder_context);
		if (vaStatus != VA_STATUS_SUCCESS)
			goto out;
		vaStatus = intel_encoder_check_jpeg_yuv_surface(ctx, profile, encode_state, encoder_context);
		break;
	}

	case VAProfileVP8Version0_3: {
		vaStatus = intel_encoder_check_vp8_parameter(ctx, encode_state, encoder_context);
		if (vaStatus != VA_STATUS_SUCCESS)
			goto out;
		vaStatus = intel_encoder_check_yuv_surface(ctx, profile, encode_state, encoder_context);
		break;
	}

	case VAProfileHEVCMain:
	case VAProfileHEVCMain10:  {
		vaStatus = intel_encoder_check_hevc_parameter(ctx, encode_state, encoder_context);
		if (vaStatus != VA_STATUS_SUCCESS)
			goto out;
		vaStatus = intel_encoder_check_yuv_surface(ctx, profile, encode_state, encoder_context);
		break;
	}

	case VAProfileVP9Profile0: {
		vaStatus = intel_encoder_check_vp9_parameter(ctx, encode_state, encoder_context);
		if (vaStatus != VA_STATUS_SUCCESS)
			goto out;
		vaStatus = intel_encoder_check_yuv_surface(ctx, profile, encode_state, encoder_context);
		break;
	}
	default:
		vaStatus = VA_STATUS_ERROR_UNSUPPORTED_PROFILE;
		break;
	}

	if (vaStatus == VA_STATUS_SUCCESS)
		vaStatus = intel_encoder_check_misc_parameter(ctx, profile, encode_state, encoder_context);

out:
	return vaStatus;
}

static VAStatus
intel_encoder_end_picture(VADriverContextP ctx,
						  VAProfile profile,
						  union codec_state *codec_state,
						  struct hw_context *hw_context)
{
	struct intel_encoder_context *encoder_context = (struct intel_encoder_context *)hw_context;
	struct encode_state *encode_state = &codec_state->encode;
	VAStatus vaStatus;

	vaStatus = intel_encoder_sanity_check_input(ctx, profile, encode_state, encoder_context);

	if (vaStatus != VA_STATUS_SUCCESS)
		return vaStatus;

	encoder_context->mfc_brc_prepare(encode_state, encoder_context);

	/* VME or PAK stages are separately invoked if middleware configured the corresponding
	 * FEI modes through confgiruation attributes. On the other hand, ENC_PAK mode
	 * will invoke both VME and PAK similar to the non fei use case.
	 * PreEnc always invoke the VME */
	if (encoder_context->fei_enabled || encoder_context->preenc_enabled) {
		if ((encoder_context->fei_function_mode == VA_FEI_FUNCTION_ENC) ||
			(encoder_context->preenc_enabled)) {
			if ((encoder_context->vme_context && encoder_context->vme_pipeline))
				return encoder_context->vme_pipeline(ctx, profile, encode_state, encoder_context);
		} else if (encoder_context->fei_function_mode == VA_FEI_FUNCTION_PAK) {
			if ((encoder_context->mfc_context && encoder_context->mfc_pipeline))
				return encoder_context->mfc_pipeline(ctx, profile, encode_state, encoder_context);
		}
		/* Setting ENC and PAK as ENC|PAK is invalid */
		assert(encoder_context->fei_function_mode != (VA_FEI_FUNCTION_ENC | VA_FEI_FUNCTION_PAK));
	}

	if ((encoder_context->vme_context && encoder_context->vme_pipeline)) {
		vaStatus = encoder_context->vme_pipeline(ctx, profile, encode_state, encoder_context);
		if (vaStatus != VA_STATUS_SUCCESS)
			return vaStatus;
	}

	assert(encoder_context->mfc_pipeline != NULL);
	encoder_context->mfc_pipeline(ctx, profile, encode_state, encoder_context);
	encoder_context->num_frames_in_sequence++;
	encoder_context->brc.need_reset = 0;
	/*
	 * ROI is only available for the current frame, see the comment
	 * for VAEncROI in va.h
	 */
	encoder_context->brc.num_roi = 0;

	return VA_STATUS_SUCCESS;
}

static void
intel_encoder_context_destroy(void *hw_context)
{
	struct intel_encoder_context *encoder_context = (struct intel_encoder_context *)hw_context;

	encoder_context->mfc_context_destroy(encoder_context->mfc_context);

	if (encoder_context->vme_context_destroy && encoder_context->vme_context)
		encoder_context->vme_context_destroy(encoder_context->vme_context);

	if (encoder_context->enc_priv_state) {
		free(encoder_context->enc_priv_state);
		encoder_context->enc_priv_state = NULL;
	}

	if (encoder_context->is_tmp_id) {
		assert(encoder_context->input_yuv_surface != VA_INVALID_SURFACE);
		i965_DestroySurfaces(encoder_context->ctx, &encoder_context->input_yuv_surface, 1);
		encoder_context->is_tmp_id = 0;
	}

	intel_batchbuffer_free(encoder_context->base.batch);
	free(encoder_context);
}


static VAStatus
intel_encoder_get_status(VADriverContextP ctx, struct hw_context *hw_context, void *buffer)
{
	struct intel_encoder_context *encoder_context = (struct intel_encoder_context *)hw_context;
	struct i965_coded_buffer_segment *coded_buffer_segment = (struct i965_coded_buffer_segment *)buffer;

	if (encoder_context->get_status)
		return encoder_context->get_status(ctx, encoder_context, coded_buffer_segment);

	return VA_STATUS_ERROR_UNIMPLEMENTED;
}

typedef Bool(* hw_init_func)(VADriverContextP, struct intel_encoder_context *);

static struct hw_context *
intel_enc_hw_context_init(VADriverContextP ctx,
						  struct object_config *obj_config,
						  hw_init_func vme_context_init,
						  hw_init_func mfc_context_init)
{
	struct i965_driver_data *i965 = i965_driver_data(ctx);
	struct intel_driver_data *intel = intel_driver_data(ctx);
	struct intel_encoder_context *encoder_context = calloc(1, sizeof(struct intel_encoder_context));
	int i;

	assert(encoder_context);
	encoder_context->base.destroy = intel_encoder_context_destroy;
	encoder_context->base.run = intel_encoder_end_picture;
	encoder_context->base.get_status = intel_encoder_get_status;
	encoder_context->base.batch = intel_batchbuffer_new(intel, I915_EXEC_RENDER, 0);
	encoder_context->input_yuv_surface = VA_INVALID_SURFACE;
	encoder_context->is_tmp_id = 0;
	encoder_context->low_power_mode = 0;
	encoder_context->rate_control_mode = VA_RC_NONE;
	encoder_context->quality_level = ENCODER_DEFAULT_QUALITY;
	encoder_context->quality_range = 1;
	encoder_context->layer.num_layers = 1;
	encoder_context->max_slice_or_seg_num = 1;
	encoder_context->ctx = ctx;

	if (obj_config->entrypoint == VAEntrypointEncSliceLP)
		encoder_context->low_power_mode = 1;

	switch (obj_config->profile) {
	case VAProfileMPEG2Simple:
	case VAProfileMPEG2Main:
		encoder_context->codec = CODEC_MPEG2;
		break;

	case VAProfileH264ConstrainedBaseline:
	case VAProfileH264Main:
	case VAProfileH264High:
		encoder_context->codec = CODEC_H264;
		/* Only supported for MPEG-2 and AVC, only support AVC. */
		encoder_context->hw_rate_control = intel->rc_hw_mode;

		if (obj_config->entrypoint == VAEntrypointEncSliceLP)
			encoder_context->quality_range = ENCODER_LP_QUALITY_RANGE;
		else if (IS_GEN9(i965->intel.device_info) ||
				 IS_GEN10(i965->intel.device_info)) {
			encoder_context->quality_level = ENCODER_DEFAULT_QUALITY_AVC;
			encoder_context->quality_range = ENCODER_QUALITY_RANGE_AVC;
		} else
			encoder_context->quality_range = ENCODER_QUALITY_RANGE;

		if (obj_config->entrypoint == VAEntrypointFEI) {
			encoder_context->fei_enabled = 1;
			/* check which attribute has been configured for FEI, this is
			 * required for VME/PAK disable or enable as per user request */
			for (i = 0; i < obj_config->num_attribs; i++) {
				if (obj_config->attrib_list[i].type == VAConfigAttribFEIFunctionType)
					encoder_context->fei_function_mode = obj_config->attrib_list[i].value;
			}
		}

		if (obj_config->entrypoint == VAEntrypointStats)
			encoder_context->preenc_enabled = 1;

		break;

	case VAProfileH264StereoHigh:
	case VAProfileH264MultiviewHigh:
		if (IS_GEN9(i965->intel.device_info) ||
			IS_GEN10(i965->intel.device_info)) {
			encoder_context->quality_level = ENCODER_DEFAULT_QUALITY_AVC;
			encoder_context->quality_range = ENCODER_QUALITY_RANGE_AVC;
		}
		encoder_context->codec = CODEC_H264_MVC;
		break;

	case VAProfileJPEGBaseline:
		encoder_context->codec = CODEC_JPEG;
		break;

	case VAProfileVP8Version0_3:
		encoder_context->codec = CODEC_VP8;
		encoder_context->quality_range = ENCODER_QUALITY_RANGE;

		break;

	case VAProfileHEVCMain:
	case VAProfileHEVCMain10:
		encoder_context->codec = CODEC_HEVC;

		encoder_context->quality_level = ENCODER_DEFAULT_QUALITY_HEVC;
		encoder_context->quality_range = ENCODER_QUALITY_RANGE_HEVC;

		encoder_context->max_slice_or_seg_num = I965_MAX_NUM_SLICE;
		break;

	case VAProfileVP9Profile0:
		encoder_context->codec = CODEC_VP9;

		if (obj_config->entrypoint == VAEntrypointEncSliceLP) {
			encoder_context->quality_level = ENCODER_DEFAULT_QUALITY_VP9;
			encoder_context->quality_range = ENCODER_QUALITY_RANGE_VP9;
		}

		break;

	default:
		/* Never get here */
		assert(0);
		break;
	}

	for (i = 0; i < obj_config->num_attribs; i++) {
		if (obj_config->attrib_list[i].type == VAConfigAttribRateControl) {
			encoder_context->rate_control_mode = obj_config->attrib_list[i].value;

			if (encoder_context->codec == CODEC_MPEG2 &&
				encoder_context->rate_control_mode & VA_RC_CBR) {
				WARN_ONCE("Don't support CBR for MPEG-2 encoding\n");
				encoder_context->rate_control_mode &= ~VA_RC_CBR;
			}
		}
		if (obj_config->attrib_list[i].type == VAConfigAttribEncROI) {
			if (encoder_context->codec == CODEC_H264)
				encoder_context->context_roi = 1;
		}
		if (obj_config->attrib_list[i].type == VAConfigAttribEncMaxSlices) {
			if (encoder_context->codec == CODEC_H264 ||
				encoder_context->codec == CODEC_HEVC)
				encoder_context->max_slice_or_seg_num = obj_config->attrib_list[i].value;
		}
	}

	if (vme_context_init) {
		vme_context_init(ctx, encoder_context);
		assert(!encoder_context->vme_context ||
			   (encoder_context->vme_context_destroy && encoder_context->vme_pipeline));
	}

	mfc_context_init(ctx, encoder_context);
	assert(encoder_context->mfc_context);
	assert(encoder_context->mfc_context_destroy);
	assert(encoder_context->mfc_pipeline);

	return (struct hw_context *)encoder_context;
}

struct hw_context *
gen6_enc_hw_context_init(VADriverContextP ctx, struct object_config *obj_config)
{
	return intel_enc_hw_context_init(ctx, obj_config, gen6_vme_context_init, gen6_mfc_context_init);
}

struct hw_context *
gen7_enc_hw_context_init(VADriverContextP ctx, struct object_config *obj_config)
{

	return intel_enc_hw_context_init(ctx, obj_config, gen7_vme_context_init, gen7_mfc_context_init);
}

struct hw_context *
gen75_enc_hw_context_init(VADriverContextP ctx, struct object_config *obj_config)
{
	return intel_enc_hw_context_init(ctx, obj_config, gen75_vme_context_init, gen75_mfc_context_init);
}

struct hw_context *
gen8_enc_hw_context_init(VADriverContextP ctx, struct object_config *obj_config)
{
	return intel_enc_hw_context_init(ctx, obj_config, gen8_vme_context_init, gen8_mfc_context_init);
}

struct hw_context *
gen9_enc_hw_context_init(VADriverContextP ctx, struct object_config *obj_config)
{
	return intel_enc_hw_context_init(ctx, obj_config, gen9_vme_context_init, gen9_mfc_context_init);
}
