// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Intel Camera Imaging ISP subsystem.
 * Copyright (c) 2015, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 */

#include <math_support.h>
#include <gdc_device.h>	/* HR_GDC_N */

#include "hmm.h"

#include "isp.h"	/* ISP_VEC_NELEMS */

#include "ia_css_binary.h"
#include "ia_css_debug.h"
#include "ia_css_util.h"
#include "ia_css_isp_param.h"
#include "sh_css_internal.h"
#include "sh_css_sp.h"
#include "sh_css_firmware.h"
#include "sh_css_defs.h"
#include "sh_css_legacy.h"

#include "atomisp_internal.h"

#include "vf/vf_1.0/ia_css_vf.host.h"
#include "sc/sc_1.0/ia_css_sc.host.h"
#include "sdis/sdis_1.0/ia_css_sdis.host.h"
#include "fixedbds/fixedbds_1.0/ia_css_fixedbds_param.h"	/* FRAC_ACC */

#include "camera/pipe/interface/ia_css_pipe_binarydesc.h"

#include "assert_support.h"

#define IMPLIES(a, b)           (!(a) || (b))   /* A => B */

static struct ia_css_binary_xinfo *all_binaries; /* ISP binaries only (no SP) */
static struct ia_css_binary_xinfo
	*binary_infos[IA_CSS_BINARY_NUM_MODES] = { NULL, };

static void
ia_css_binary_dvs_env(const struct ia_css_binary_info *info,
		      const struct ia_css_resolution *dvs_env,
		      struct ia_css_resolution *binary_dvs_env)
{
	if (info->enable.dvs_envelope) {
		assert(dvs_env);
		binary_dvs_env->width  = max(dvs_env->width, SH_CSS_MIN_DVS_ENVELOPE);
		binary_dvs_env->height = max(dvs_env->height, SH_CSS_MIN_DVS_ENVELOPE);
	}
}

static void
ia_css_binary_internal_res(const struct ia_css_frame_info *in_info,
			   const struct ia_css_frame_info *bds_out_info,
			   const struct ia_css_frame_info *out_info,
			   const struct ia_css_resolution *dvs_env,
			   const struct ia_css_binary_info *info,
			   struct ia_css_resolution *internal_res)
{
	unsigned int isp_tmp_internal_width = 0,
		     isp_tmp_internal_height = 0;
	bool binary_supports_yuv_ds = info->enable.ds & 2;
	struct ia_css_resolution binary_dvs_env;

	binary_dvs_env.width = 0;
	binary_dvs_env.height = 0;
	ia_css_binary_dvs_env(info, dvs_env, &binary_dvs_env);

	if (binary_supports_yuv_ds) {
		if (in_info) {
			isp_tmp_internal_width = in_info->res.width
						 + info->pipeline.left_cropping + binary_dvs_env.width;
			isp_tmp_internal_height = in_info->res.height
						  + info->pipeline.top_cropping + binary_dvs_env.height;
		}
	} else if ((bds_out_info) && (out_info) &&
		   /* TODO: hack to make video_us case work. this should be reverted after
		   a nice solution in ISP */
		   (bds_out_info->res.width >= out_info->res.width)) {
		isp_tmp_internal_width = bds_out_info->padded_width;
		isp_tmp_internal_height = bds_out_info->res.height;
	} else {
		if (out_info) {
			isp_tmp_internal_width = out_info->padded_width;
			isp_tmp_internal_height = out_info->res.height;
		}
	}

	/* We first calculate the resolutions used by the ISP. After that,
	 * we use those resolutions to compute sizes for tables etc. */
	internal_res->width = __ISP_INTERNAL_WIDTH(isp_tmp_internal_width,
			      (int)binary_dvs_env.width,
			      info->pipeline.left_cropping, info->pipeline.mode,
			      info->pipeline.c_subsampling,
			      info->output.num_chunks, info->pipeline.pipelining);
	internal_res->height = __ISP_INTERNAL_HEIGHT(isp_tmp_internal_height,
			       info->pipeline.top_cropping,
			       binary_dvs_env.height);
}

/* ISP2400 */
/* Computation results of the origin coordinate of bayer on the shading table. */
struct sh_css_shading_table_bayer_origin_compute_results {
	u32 bayer_scale_hor_ratio_in;	/* Horizontal ratio (in) of bayer scaling. */
	u32 bayer_scale_hor_ratio_out;	/* Horizontal ratio (out) of bayer scaling. */
	u32 bayer_scale_ver_ratio_in;	/* Vertical ratio (in) of bayer scaling. */
	u32 bayer_scale_ver_ratio_out;	/* Vertical ratio (out) of bayer scaling. */
	u32 sc_bayer_origin_x_bqs_on_shading_table; /* X coordinate (in bqs) of bayer origin on shading table. */
	u32 sc_bayer_origin_y_bqs_on_shading_table; /* Y coordinate (in bqs) of bayer origin on shading table. */
};

/* ISP2401 */
/* Requirements for the shading correction. */
struct sh_css_binary_sc_requirements {
	/* Bayer scaling factor, for the scaling which is applied before shading correction. */
	u32 bayer_scale_hor_ratio_in;  /* Horizontal ratio (in) of scaling applied BEFORE shading correction. */
	u32 bayer_scale_hor_ratio_out; /* Horizontal ratio (out) of scaling applied BEFORE shading correction. */
	u32 bayer_scale_ver_ratio_in;  /* Vertical ratio (in) of scaling applied BEFORE shading correction. */
	u32 bayer_scale_ver_ratio_out; /* Vertical ratio (out) of scaling applied BEFORE shading correction. */

	/* ISP internal frame is composed of the real sensor data and the padding data. */
	u32 sensor_data_origin_x_bqs_on_internal; /* X origin (in bqs) of sensor data on internal frame
								at shading correction. */
	u32 sensor_data_origin_y_bqs_on_internal; /* Y origin (in bqs) of sensor data on internal frame
								at shading correction. */
};

/* ISP2400: Get the requirements for the shading correction. */
static int
ia_css_binary_compute_shading_table_bayer_origin(
    const struct ia_css_binary *binary,				/* [in] */
    unsigned int required_bds_factor,				/* [in] */
    const struct ia_css_stream_config *stream_config,		/* [in] */
    struct sh_css_shading_table_bayer_origin_compute_results *res)	/* [out] */
{
	int err;

	/* Numerator and denominator of the fixed bayer downscaling factor.
	(numerator >= denominator) */
	unsigned int bds_num, bds_den;

	/* Horizontal/Vertical ratio of bayer scaling
	between input area and output area. */
	unsigned int bs_hor_ratio_in;
	unsigned int bs_hor_ratio_out;
	unsigned int bs_ver_ratio_in;
	unsigned int bs_ver_ratio_out;

	/* Left padding set by InputFormatter. */
	unsigned int left_padding_bqs;			/* in bqs */

	/* Flag for the NEED_BDS_FACTOR_2_00 macro defined in isp kernels. */
	unsigned int need_bds_factor_2_00;

	/* Left padding adjusted inside the isp. */
	unsigned int left_padding_adjusted_bqs;		/* in bqs */

	/* Bad pixels caused by filters.
	NxN-filter (before/after bayer scaling) moves the image position
	to right/bottom directions by a few pixels.
	It causes bad pixels at left/top sides,
	and effective bayer size decreases. */
	unsigned int bad_bqs_on_left_before_bs;	/* in bqs */
	unsigned int bad_bqs_on_left_after_bs;	/* in bqs */
	unsigned int bad_bqs_on_top_before_bs;	/* in bqs */
	unsigned int bad_bqs_on_top_after_bs;	/* in bqs */

	/* Get the numerator and denominator of bayer downscaling factor. */
	err = sh_css_bds_factor_get_numerator_denominator
	(required_bds_factor, &bds_num, &bds_den);
	if (err)
		return err;

	/* Set the horizontal/vertical ratio of bayer scaling
	between input area and output area. */
	bs_hor_ratio_in  = bds_num;
	bs_hor_ratio_out = bds_den;
	bs_ver_ratio_in  = bds_num;
	bs_ver_ratio_out = bds_den;

	/* Set the left padding set by InputFormatter. (ifmtr.c) */
	if (stream_config->left_padding == -1)
		left_padding_bqs = _ISP_BQS(binary->left_padding);
	else
		left_padding_bqs = (unsigned int)((int)ISP_VEC_NELEMS
				   - _ISP_BQS(stream_config->left_padding));

	/* Set the left padding adjusted inside the isp.
	When bds_factor 2.00 is needed, some padding is added to left_padding
	inside the isp, before bayer downscaling. (raw.isp.c)
	(Hopefully, left_crop/left_padding/top_crop should be defined in css
	appropriately, depending on bds_factor.)
	*/
	need_bds_factor_2_00 = ((binary->info->sp.bds.supported_bds_factors &
				(PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_2_00) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_2_50) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_3_00) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_4_00) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_4_50) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_5_00) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_6_00) |
				 PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_8_00))) != 0);

	if (need_bds_factor_2_00 && binary->info->sp.pipeline.left_cropping > 0)
		left_padding_adjusted_bqs = left_padding_bqs + ISP_VEC_NELEMS;
	else
		left_padding_adjusted_bqs = left_padding_bqs;

	/* Currently, the bad pixel caused by filters before bayer scaling
	is NOT considered, because the bad pixel is subtle.
	When some large filter is used in the future,
	we need to consider the bad pixel.

	Currently, when bds_factor isn't 1.00, 3x3 anti-alias filter is applied
	to each color plane(Gr/R/B/Gb) before bayer downscaling.
	This filter moves each color plane to right/bottom directions
	by 1 pixel at the most, depending on downscaling factor.
	*/
	bad_bqs_on_left_before_bs = 0;
	bad_bqs_on_top_before_bs = 0;

	/* Currently, the bad pixel caused by filters after bayer scaling
	is NOT considered, because the bad pixel is subtle.
	When some large filter is used in the future,
	we need to consider the bad pixel.

	Currently, when DPC&BNR is processed between bayer scaling and
	shading correction, DPC&BNR moves each color plane to
	right/bottom directions by 1 pixel.
	*/
	bad_bqs_on_left_after_bs = 0;
	bad_bqs_on_top_after_bs = 0;

	/* Calculate the origin of bayer (real sensor data area)
	located on the shading table during the shading correction. */
	res->sc_bayer_origin_x_bqs_on_shading_table =
		((left_padding_adjusted_bqs + bad_bqs_on_left_before_bs)
		* bs_hor_ratio_out + bs_hor_ratio_in / 2) / bs_hor_ratio_in
		+ bad_bqs_on_left_after_bs;
	/* "+ bs_hor_ratio_in/2": rounding for division by bs_hor_ratio_in */
	res->sc_bayer_origin_y_bqs_on_shading_table =
		(bad_bqs_on_top_before_bs * bs_ver_ratio_out + bs_ver_ratio_in / 2) / bs_ver_ratio_in
		+ bad_bqs_on_top_after_bs;
	/* "+ bs_ver_ratio_in/2": rounding for division by bs_ver_ratio_in */

	res->bayer_scale_hor_ratio_in  = (uint32_t)bs_hor_ratio_in;
	res->bayer_scale_hor_ratio_out = (uint32_t)bs_hor_ratio_out;
	res->bayer_scale_ver_ratio_in  = (uint32_t)bs_ver_ratio_in;
	res->bayer_scale_ver_ratio_out = (uint32_t)bs_ver_ratio_out;

	return err;
}

/* ISP2401: Get the requirements for the shading correction. */
static int
sh_css_binary_get_sc_requirements(const struct ia_css_binary *binary, /* [in] */
				  unsigned int required_bds_factor,   /* [in] */
				  const struct ia_css_stream_config *stream_config, /* [in] */
				  struct sh_css_binary_sc_requirements *scr) /* [out] */
{
	int err;

	/* Numerator and denominator of the fixed bayer downscaling factor. (numerator >= denominator) */
	unsigned int bds_num, bds_den;

	/* Horizontal/Vertical ratio of bayer scaling between input area and output area. */
	unsigned int bs_hor_ratio_in, bs_hor_ratio_out, bs_ver_ratio_in, bs_ver_ratio_out;

	/* Left padding set by InputFormatter. */
	unsigned int left_padding_bqs;

	/* Flags corresponding to NEED_BDS_FACTOR_2_00/NEED_BDS_FACTOR_1_50/NEED_BDS_FACTOR_1_25 macros
	 * defined in isp kernels. */
	unsigned int need_bds_factor_2_00, need_bds_factor_1_50, need_bds_factor_1_25;

	/* Left padding adjusted inside the isp kernels. */
	unsigned int left_padding_adjusted_bqs;

	/* Top padding padded inside the isp kernel for bayer downscaling binaries. */
	unsigned int top_padding_bqs;

	/* Bayer downscaling factor 1.0 by fixed-point. */
	int bds_frac_acc = FRAC_ACC;	/* FRAC_ACC is defined in ia_css_fixedbds_param.h. */

	/* Right/Down shift amount caused by filters applied BEFORE shading corrertion. */
	unsigned int right_shift_bqs_before_bs; /* right shift before bayer scaling */
	unsigned int right_shift_bqs_after_bs;  /* right shift after bayer scaling */
	unsigned int down_shift_bqs_before_bs;  /* down shift before bayer scaling */
	unsigned int down_shift_bqs_after_bs;   /* down shift after bayer scaling */

	/* Origin of the real sensor data area on the internal frame at shading correction. */
	unsigned int sensor_data_origin_x_bqs_on_internal;
	unsigned int sensor_data_origin_y_bqs_on_internal;

	unsigned int bs_frac = bds_frac_acc;	/* scaling factor 1.0 in fixed point */
	unsigned int bs_out, bs_in;		/* scaling ratio in fixed point */

	IA_CSS_ENTER_PRIVATE("binary=%p, required_bds_factor=%d, stream_config=%p",
			     binary, required_bds_factor, stream_config);

	/* Get the numerator and denominator of the required bayer downscaling factor. */
	err = sh_css_bds_factor_get_numerator_denominator(required_bds_factor,
							  &bds_num, &bds_den);
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}

	IA_CSS_LOG("bds_num=%d, bds_den=%d", bds_num, bds_den);

	/* Set the horizontal/vertical ratio of bayer scaling between input area and output area. */
	bs_hor_ratio_in  = bds_num;
	bs_hor_ratio_out = bds_den;
	bs_ver_ratio_in  = bds_num;
	bs_ver_ratio_out = bds_den;

	/* Set the left padding set by InputFormatter. (ia_css_ifmtr_configure() in ifmtr.c) */
	if (stream_config->left_padding == -1)
		left_padding_bqs = _ISP_BQS(binary->left_padding);
	else
		left_padding_bqs = (unsigned int)((int)ISP_VEC_NELEMS - _ISP_BQS(stream_config->left_padding));

	IA_CSS_LOG("stream.left_padding=%d, binary.left_padding=%d, left_padding_bqs=%d",
		   stream_config->left_padding, binary->left_padding,
		   left_padding_bqs);

	/* Set the left padding adjusted inside the isp kernels.
	* When the bds_factor isn't 1.00, the left padding size is adjusted inside the isp,
	* before bayer downscaling. (scaled_hor_plane_index(), raw_compute_hphase() in raw.isp.c)
	*/
	need_bds_factor_2_00 = ((binary->info->sp.bds.supported_bds_factors &
				(PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_2_00) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_2_50) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_3_00) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_4_00) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_4_50) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_5_00) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_6_00) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_8_00))) != 0);

	need_bds_factor_1_50 = ((binary->info->sp.bds.supported_bds_factors &
				(PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_1_50) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_2_25) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_3_00) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_4_50) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_6_00))) != 0);

	need_bds_factor_1_25 = ((binary->info->sp.bds.supported_bds_factors &
				(PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_1_25) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_2_50) |
				PACK_BDS_FACTOR(SH_CSS_BDS_FACTOR_5_00))) != 0);

	if (binary->info->sp.pipeline.left_cropping > 0 &&
	    (need_bds_factor_2_00 || need_bds_factor_1_50 || need_bds_factor_1_25)) {
		/*
		* downscale 2.0  -> first_vec_adjusted_bqs = 128
		* downscale 1.5  -> first_vec_adjusted_bqs = 96
		* downscale 1.25 -> first_vec_adjusted_bqs = 80
		*/
		unsigned int first_vec_adjusted_bqs = ISP_VEC_NELEMS * bs_hor_ratio_in / bs_hor_ratio_out;
		left_padding_adjusted_bqs = first_vec_adjusted_bqs
			    - _ISP_BQS(binary->info->sp.pipeline.left_cropping);
	} else {
		left_padding_adjusted_bqs = left_padding_bqs;
	}

	IA_CSS_LOG("supported_bds_factors=%d, need_bds_factor:2_00=%d, 1_50=%d, 1_25=%d",
		   binary->info->sp.bds.supported_bds_factors,
		   need_bds_factor_2_00, need_bds_factor_1_50,
		   need_bds_factor_1_25);
	IA_CSS_LOG("left_cropping=%d, left_padding_adjusted_bqs=%d",
		   binary->info->sp.pipeline.left_cropping,
		   left_padding_adjusted_bqs);

	/* Set the top padding padded inside the isp kernel for bayer downscaling binaries.
	* When the bds_factor isn't 1.00, the top padding is padded inside the isp
	* before bayer downscaling, because the top cropping size (input margin) is not enough.
	* (calculate_input_line(), raw_compute_vphase(), dma_read_raw() in raw.isp.c)
	* NOTE: In dma_read_raw(), the factor passed to raw_compute_vphase() is got by get_bds_factor_for_dma_read().
	*       This factor is BDS_FPVAL_100/BDS_FPVAL_125/BDS_FPVAL_150/BDS_FPVAL_200.
	*/
	top_padding_bqs = 0;
	if (binary->info->sp.pipeline.top_cropping > 0 &&
	    (required_bds_factor == SH_CSS_BDS_FACTOR_1_25 ||
	    required_bds_factor == SH_CSS_BDS_FACTOR_1_50 ||
	    required_bds_factor == SH_CSS_BDS_FACTOR_2_00)) {
		/* Calculation from calculate_input_line() and raw_compute_vphase() in raw.isp.c. */
		int top_cropping_bqs = _ISP_BQS(binary->info->sp.pipeline.top_cropping);
		/* top cropping (in bqs) */
		int factor = bds_num * bds_frac_acc /
		bds_den;	/* downscaling factor by fixed-point */
		int top_padding_bqsxfrac_acc = (top_cropping_bqs * factor - top_cropping_bqs *
						bds_frac_acc)
		+ (2 * bds_frac_acc - factor);	/* top padding by fixed-point (in bqs) */

		top_padding_bqs = (unsigned int)((top_padding_bqsxfrac_acc + bds_frac_acc / 2 -
						1) / bds_frac_acc);
	}

	IA_CSS_LOG("top_cropping=%d, top_padding_bqs=%d",
		   binary->info->sp.pipeline.top_cropping, top_padding_bqs);

	/* Set the right/down shift amount caused by filters applied BEFORE bayer scaling,
	* which scaling is applied BEFORE shading corrertion.
	*
	* When the bds_factor isn't 1.00, 3x3 anti-alias filter is applied to each color plane(Gr/R/B/Gb)
	* before bayer downscaling.
	* This filter shifts each color plane (Gr/R/B/Gb) to right/down directions by 1 pixel.
	*/
	right_shift_bqs_before_bs = 0;
	down_shift_bqs_before_bs = 0;

	if (need_bds_factor_2_00 || need_bds_factor_1_50 || need_bds_factor_1_25) {
		right_shift_bqs_before_bs = 1;
		down_shift_bqs_before_bs = 1;
	}

	IA_CSS_LOG("right_shift_bqs_before_bs=%d, down_shift_bqs_before_bs=%d",
		   right_shift_bqs_before_bs, down_shift_bqs_before_bs);

	/* Set the right/down shift amount caused by filters applied AFTER bayer scaling,
	* which scaling is applied BEFORE shading corrertion.
	*
	* When DPC&BNR is processed between bayer scaling and shading correction,
	* DPC&BNR moves each color plane (Gr/R/B/Gb) to right/down directions by 1 pixel.
	*/
	right_shift_bqs_after_bs = 0;
	down_shift_bqs_after_bs = 0;

	/* if DPC&BNR is enabled in the binary */
	if (binary->info->mem_offsets.offsets.param->dmem.dp.size != 0) {
		right_shift_bqs_after_bs = 1;
		down_shift_bqs_after_bs = 1;
	}

	IA_CSS_LOG("right_shift_bqs_after_bs=%d, down_shift_bqs_after_bs=%d",
		   right_shift_bqs_after_bs, down_shift_bqs_after_bs);

	bs_out = bs_hor_ratio_out * bs_frac;
	bs_in = bs_hor_ratio_in * bs_frac;
	sensor_data_origin_x_bqs_on_internal =
		((left_padding_adjusted_bqs + right_shift_bqs_before_bs) * bs_out + bs_in / 2) / bs_in
		+ right_shift_bqs_after_bs;	/* "+ bs_in/2": rounding */

	bs_out = bs_ver_ratio_out * bs_frac;
	bs_in = bs_ver_ratio_in * bs_frac;
	sensor_data_origin_y_bqs_on_internal =
		((top_padding_bqs + down_shift_bqs_before_bs) * bs_out + bs_in / 2) / bs_in
		+ down_shift_bqs_after_bs;	/* "+ bs_in/2": rounding */

	scr->bayer_scale_hor_ratio_in			= (uint32_t)bs_hor_ratio_in;
	scr->bayer_scale_hor_ratio_out			= (uint32_t)bs_hor_ratio_out;
	scr->bayer_scale_ver_ratio_in			= (uint32_t)bs_ver_ratio_in;
	scr->bayer_scale_ver_ratio_out			= (uint32_t)bs_ver_ratio_out;
	scr->sensor_data_origin_x_bqs_on_internal	= (uint32_t)sensor_data_origin_x_bqs_on_internal;
	scr->sensor_data_origin_y_bqs_on_internal	= (uint32_t)sensor_data_origin_y_bqs_on_internal;

	IA_CSS_LOG("sc_requirements: %d, %d, %d, %d, %d, %d",
		   scr->bayer_scale_hor_ratio_in,
		   scr->bayer_scale_hor_ratio_out,
		   scr->bayer_scale_ver_ratio_in, scr->bayer_scale_ver_ratio_out,
		   scr->sensor_data_origin_x_bqs_on_internal,
		   scr->sensor_data_origin_y_bqs_on_internal);

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/* Get the shading information of Shading Correction Type 1. */
static int
isp2400_binary_get_shading_info_type_1(const struct ia_css_binary *binary,	/* [in] */
				       unsigned int required_bds_factor,			/* [in] */
				       const struct ia_css_stream_config *stream_config,	/* [in] */
				       struct ia_css_shading_info *info)			/* [out] */
{
	int err;
	struct sh_css_shading_table_bayer_origin_compute_results res;

	assert(binary);
	assert(info);

	info->type = IA_CSS_SHADING_CORRECTION_TYPE_1;

	info->info.type_1.enable	    = binary->info->sp.enable.sc;
	info->info.type_1.num_hor_grids	    = binary->sctbl_width_per_color;
	info->info.type_1.num_ver_grids	    = binary->sctbl_height;
	info->info.type_1.bqs_per_grid_cell = (1 << binary->deci_factor_log2);

	/* Initialize by default values. */
	info->info.type_1.bayer_scale_hor_ratio_in	= 1;
	info->info.type_1.bayer_scale_hor_ratio_out	= 1;
	info->info.type_1.bayer_scale_ver_ratio_in	= 1;
	info->info.type_1.bayer_scale_ver_ratio_out	= 1;
	info->info.type_1.sc_bayer_origin_x_bqs_on_shading_table = 0;
	info->info.type_1.sc_bayer_origin_y_bqs_on_shading_table = 0;

	err = ia_css_binary_compute_shading_table_bayer_origin(
	    binary,
	    required_bds_factor,
	    stream_config,
	    &res);
	if (err)
		return err;

	info->info.type_1.bayer_scale_hor_ratio_in	= res.bayer_scale_hor_ratio_in;
	info->info.type_1.bayer_scale_hor_ratio_out	= res.bayer_scale_hor_ratio_out;
	info->info.type_1.bayer_scale_ver_ratio_in	= res.bayer_scale_ver_ratio_in;
	info->info.type_1.bayer_scale_ver_ratio_out	= res.bayer_scale_ver_ratio_out;
	info->info.type_1.sc_bayer_origin_x_bqs_on_shading_table = res.sc_bayer_origin_x_bqs_on_shading_table;
	info->info.type_1.sc_bayer_origin_y_bqs_on_shading_table = res.sc_bayer_origin_y_bqs_on_shading_table;

	return err;
}

/* Get the shading information of Shading Correction Type 1. */
static int
isp2401_binary_get_shading_info_type_1(const struct ia_css_binary *binary,	/* [in] */
				       unsigned int required_bds_factor,			/* [in] */
				       const struct ia_css_stream_config *stream_config,	/* [in] */
				       struct ia_css_shading_info *shading_info,		/* [out] */
				       struct ia_css_pipe_config *pipe_config)			/* [out] */
{
	int err;
	struct sh_css_binary_sc_requirements scr;

	u32 in_width_bqs, in_height_bqs, internal_width_bqs, internal_height_bqs;
	u32 num_hor_grids, num_ver_grids, bqs_per_grid_cell, tbl_width_bqs, tbl_height_bqs;
	u32 sensor_org_x_bqs_on_internal, sensor_org_y_bqs_on_internal, sensor_width_bqs, sensor_height_bqs;
	u32 sensor_center_x_bqs_on_internal, sensor_center_y_bqs_on_internal;
	u32 left, right, upper, lower;
	u32 adjust_left, adjust_right, adjust_upper, adjust_lower, adjust_width_bqs, adjust_height_bqs;
	u32 internal_org_x_bqs_on_tbl, internal_org_y_bqs_on_tbl;
	u32 sensor_org_x_bqs_on_tbl, sensor_org_y_bqs_on_tbl;

	assert(binary);
	assert(stream_config);
	assert(shading_info);
	assert(pipe_config);

	IA_CSS_ENTER_PRIVATE("binary=%p, required_bds_factor=%d, stream_config=%p",
			     binary, required_bds_factor, stream_config);

	/* Initialize by default values. */
	*shading_info = DEFAULT_SHADING_INFO_TYPE_1;

	err = sh_css_binary_get_sc_requirements(binary, required_bds_factor, stream_config, &scr);
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}

	IA_CSS_LOG("binary: id=%d, sctbl=%dx%d, deci=%d",
		binary->info->sp.id, binary->sctbl_width_per_color, binary->sctbl_height, binary->deci_factor_log2);
	IA_CSS_LOG("binary: in=%dx%d, in_padded_w=%d, int=%dx%d, int_padded_w=%d, out=%dx%d, out_padded_w=%d",
		binary->in_frame_info.res.width, binary->in_frame_info.res.height, binary->in_frame_info.padded_width,
		binary->internal_frame_info.res.width, binary->internal_frame_info.res.height,
		binary->internal_frame_info.padded_width,
		binary->out_frame_info[0].res.width, binary->out_frame_info[0].res.height,
		binary->out_frame_info[0].padded_width);

	/* Set the input size from sensor, which includes left/top crop size. */
	in_width_bqs	    = _ISP_BQS(binary->in_frame_info.res.width);
	in_height_bqs	    = _ISP_BQS(binary->in_frame_info.res.height);

	/*
	 * Frame size internally used in ISP, including sensor data and padding.
	 * This is the frame size, to which the shading correction is applied.
	 */
	internal_width_bqs  = _ISP_BQS(binary->internal_frame_info.res.width);
	internal_height_bqs = _ISP_BQS(binary->internal_frame_info.res.height);

	/* Shading table. */
	num_hor_grids = binary->sctbl_width_per_color;
	num_ver_grids = binary->sctbl_height;
	bqs_per_grid_cell = (1 << binary->deci_factor_log2);
	tbl_width_bqs  = (num_hor_grids - 1) * bqs_per_grid_cell;
	tbl_height_bqs = (num_ver_grids - 1) * bqs_per_grid_cell;

	IA_CSS_LOG("tbl_width_bqs=%d, tbl_height_bqs=%d", tbl_width_bqs, tbl_height_bqs);

	/*
	 * Real sensor data area on the internal frame at shading correction.
	 * Filters and scaling are applied to the internal frame before
	 * shading correction, depending on the binary.
	 */
	sensor_org_x_bqs_on_internal = scr.sensor_data_origin_x_bqs_on_internal;
	sensor_org_y_bqs_on_internal = scr.sensor_data_origin_y_bqs_on_internal;
	{
		unsigned int bs_frac = 8;	/* scaling factor 1.0 in fixed point (8 == FRAC_ACC macro in ISP) */
		unsigned int bs_out, bs_in;	/* scaling ratio in fixed point */

		bs_out = scr.bayer_scale_hor_ratio_out * bs_frac;
		bs_in = scr.bayer_scale_hor_ratio_in * bs_frac;
		sensor_width_bqs  = (in_width_bqs * bs_out + bs_in / 2) / bs_in; /* "+ bs_in/2": rounding */

		bs_out = scr.bayer_scale_ver_ratio_out * bs_frac;
		bs_in = scr.bayer_scale_ver_ratio_in * bs_frac;
		sensor_height_bqs = (in_height_bqs * bs_out + bs_in / 2) / bs_in; /* "+ bs_in/2": rounding */
	}

	/* Center of the sensor data on the internal frame at shading correction. */
	sensor_center_x_bqs_on_internal = sensor_org_x_bqs_on_internal + sensor_width_bqs / 2;
	sensor_center_y_bqs_on_internal = sensor_org_y_bqs_on_internal + sensor_height_bqs / 2;

	/* Size of left/right/upper/lower sides of the sensor center on the internal frame. */
	left  = sensor_center_x_bqs_on_internal;
	right = internal_width_bqs - sensor_center_x_bqs_on_internal;
	upper = sensor_center_y_bqs_on_internal;
	lower = internal_height_bqs - sensor_center_y_bqs_on_internal;

	/* Align the size of left/right/upper/lower sides to a multiple of the grid cell size. */
	adjust_left  = CEIL_MUL(left,  bqs_per_grid_cell);
	adjust_right = CEIL_MUL(right, bqs_per_grid_cell);
	adjust_upper = CEIL_MUL(upper, bqs_per_grid_cell);
	adjust_lower = CEIL_MUL(lower, bqs_per_grid_cell);

	/* Shading table should cover the adjusted frame size. */
	adjust_width_bqs  = adjust_left + adjust_right;
	adjust_height_bqs = adjust_upper + adjust_lower;

	IA_CSS_LOG("adjust_width_bqs=%d, adjust_height_bqs=%d", adjust_width_bqs, adjust_height_bqs);

	if (adjust_width_bqs > tbl_width_bqs || adjust_height_bqs > tbl_height_bqs) {
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	/* Origin of the internal frame on the shading table. */
	internal_org_x_bqs_on_tbl = adjust_left - left;
	internal_org_y_bqs_on_tbl = adjust_upper - upper;

	/* Origin of the real sensor data area on the shading table. */
	sensor_org_x_bqs_on_tbl = internal_org_x_bqs_on_tbl + sensor_org_x_bqs_on_internal;
	sensor_org_y_bqs_on_tbl = internal_org_y_bqs_on_tbl + sensor_org_y_bqs_on_internal;

	/* The shading information necessary as API is stored in the shading_info. */
	shading_info->info.type_1.num_hor_grids	    = num_hor_grids;
	shading_info->info.type_1.num_ver_grids	    = num_ver_grids;
	shading_info->info.type_1.bqs_per_grid_cell = bqs_per_grid_cell;

	shading_info->info.type_1.bayer_scale_hor_ratio_in  = scr.bayer_scale_hor_ratio_in;
	shading_info->info.type_1.bayer_scale_hor_ratio_out = scr.bayer_scale_hor_ratio_out;
	shading_info->info.type_1.bayer_scale_ver_ratio_in  = scr.bayer_scale_ver_ratio_in;
	shading_info->info.type_1.bayer_scale_ver_ratio_out = scr.bayer_scale_ver_ratio_out;

	shading_info->info.type_1.isp_input_sensor_data_res_bqs.width  = in_width_bqs;
	shading_info->info.type_1.isp_input_sensor_data_res_bqs.height = in_height_bqs;

	shading_info->info.type_1.sensor_data_res_bqs.width  = sensor_width_bqs;
	shading_info->info.type_1.sensor_data_res_bqs.height = sensor_height_bqs;

	shading_info->info.type_1.sensor_data_origin_bqs_on_sctbl.x = (int32_t)sensor_org_x_bqs_on_tbl;
	shading_info->info.type_1.sensor_data_origin_bqs_on_sctbl.y = (int32_t)sensor_org_y_bqs_on_tbl;

	/* The shading information related to ISP (but, not necessary as API) is stored in the pipe_config. */
	pipe_config->internal_frame_origin_bqs_on_sctbl.x = (int32_t)internal_org_x_bqs_on_tbl;
	pipe_config->internal_frame_origin_bqs_on_sctbl.y = (int32_t)internal_org_y_bqs_on_tbl;

	IA_CSS_LOG("shading_info: grids=%dx%d, cell=%d, scale=%d,%d,%d,%d, input=%dx%d, data=%dx%d, origin=(%d,%d)",
		   shading_info->info.type_1.num_hor_grids,
		   shading_info->info.type_1.num_ver_grids,
		   shading_info->info.type_1.bqs_per_grid_cell,
		   shading_info->info.type_1.bayer_scale_hor_ratio_in,
		   shading_info->info.type_1.bayer_scale_hor_ratio_out,
		   shading_info->info.type_1.bayer_scale_ver_ratio_in,
		   shading_info->info.type_1.bayer_scale_ver_ratio_out,
		   shading_info->info.type_1.isp_input_sensor_data_res_bqs.width,
		   shading_info->info.type_1.isp_input_sensor_data_res_bqs.height,
		   shading_info->info.type_1.sensor_data_res_bqs.width,
		   shading_info->info.type_1.sensor_data_res_bqs.height,
		   shading_info->info.type_1.sensor_data_origin_bqs_on_sctbl.x,
		   shading_info->info.type_1.sensor_data_origin_bqs_on_sctbl.y);

	IA_CSS_LOG("pipe_config: origin=(%d,%d)",
		   pipe_config->internal_frame_origin_bqs_on_sctbl.x,
		   pipe_config->internal_frame_origin_bqs_on_sctbl.y);

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}


int
ia_css_binary_get_shading_info(const struct ia_css_binary *binary,			/* [in] */
			       enum ia_css_shading_correction_type type,		/* [in] */
			       unsigned int required_bds_factor,			/* [in] */
			       const struct ia_css_stream_config *stream_config,	/* [in] */
			       struct ia_css_shading_info *shading_info,		/* [out] */
			       struct ia_css_pipe_config *pipe_config)			/* [out] */
{
	int err;

	assert(binary);
	assert(shading_info);

	IA_CSS_ENTER_PRIVATE("binary=%p, type=%d, required_bds_factor=%d, stream_config=%p",
			     binary, type, required_bds_factor, stream_config);

	if (type != IA_CSS_SHADING_CORRECTION_TYPE_1) {
		err = -ENOTSUPP;

		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}

	if (!IS_ISP2401)
		err = isp2400_binary_get_shading_info_type_1(binary,
							     required_bds_factor,
							     stream_config,
							     shading_info);
	else
		err = isp2401_binary_get_shading_info_type_1(binary,
							     required_bds_factor,
							     stream_config,
							     shading_info,
							     pipe_config);

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static void sh_css_binary_common_grid_info(const struct ia_css_binary *binary,
	struct ia_css_grid_info *info)
{
	assert(binary);
	assert(info);

	info->isp_in_width = binary->internal_frame_info.res.width;
	info->isp_in_height = binary->internal_frame_info.res.height;

	info->vamem_type = IA_CSS_VAMEM_TYPE_2;
}

void
ia_css_binary_dvs_grid_info(const struct ia_css_binary *binary,
			    struct ia_css_grid_info *info,
			    struct ia_css_pipe *pipe)
{
	struct ia_css_dvs_grid_info *dvs_info;

	(void)pipe;
	assert(binary);
	assert(info);

	dvs_info = &info->dvs_grid.dvs_grid_info;

	/* for DIS, we use a division instead of a ceil_div. If this is smaller
	 * than the 3a grid size, it indicates that the outer values are not
	 * valid for DIS.
	 */
	dvs_info->enable            = binary->info->sp.enable.dis;
	dvs_info->width             = binary->dis.grid.dim.width;
	dvs_info->height            = binary->dis.grid.dim.height;
	dvs_info->aligned_width     = binary->dis.grid.pad.width;
	dvs_info->aligned_height    = binary->dis.grid.pad.height;
	dvs_info->bqs_per_grid_cell = 1 << binary->dis.deci_factor_log2;
	dvs_info->num_hor_coefs     = binary->dis.coef.dim.width;
	dvs_info->num_ver_coefs     = binary->dis.coef.dim.height;

	sh_css_binary_common_grid_info(binary, info);
}

void
ia_css_binary_dvs_stat_grid_info(
    const struct ia_css_binary *binary,
    struct ia_css_grid_info *info,
    struct ia_css_pipe *pipe)
{
	(void)pipe;
	sh_css_binary_common_grid_info(binary, info);
	return;
}

int
ia_css_binary_3a_grid_info(const struct ia_css_binary *binary,
			   struct ia_css_grid_info *info,
			   struct ia_css_pipe *pipe) {
	struct ia_css_3a_grid_info *s3a_info;
	int err = 0;

	IA_CSS_ENTER_PRIVATE("binary=%p, info=%p, pipe=%p",
			     binary, info, pipe);

	assert(binary);
	assert(info);
	s3a_info = &info->s3a_grid;

	/* 3A statistics grid */
	s3a_info->enable            = binary->info->sp.enable.s3a;
	s3a_info->width             = binary->s3atbl_width;
	s3a_info->height            = binary->s3atbl_height;
	s3a_info->aligned_width     = binary->s3atbl_isp_width;
	s3a_info->aligned_height    = binary->s3atbl_isp_height;
	s3a_info->bqs_per_grid_cell = (1 << binary->deci_factor_log2);
	s3a_info->deci_factor_log2  = binary->deci_factor_log2;
	s3a_info->elem_bit_depth    = SH_CSS_BAYER_BITS;
	s3a_info->use_dmem          = binary->info->sp.s3a.s3atbl_use_dmem;
#if defined(HAS_NO_HMEM)
	s3a_info->has_histogram     = 1;
#else
	s3a_info->has_histogram     = 0;
#endif
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static void
binary_init_pc_histogram(struct sh_css_pc_histogram *histo)
{
	assert(histo);

	histo->length = 0;
	histo->run = NULL;
	histo->stall = NULL;
}

static void
binary_init_metrics(struct sh_css_binary_metrics *metrics,
		    const struct ia_css_binary_info *info)
{
	assert(metrics);
	assert(info);

	metrics->mode = info->pipeline.mode;
	metrics->id   = info->id;
	metrics->next = NULL;
	binary_init_pc_histogram(&metrics->isp_histogram);
	binary_init_pc_histogram(&metrics->sp_histogram);
}

/* move to host part of output module */
static bool
binary_supports_output_format(const struct ia_css_binary_xinfo *info,
			      enum ia_css_frame_format format)
{
	int i;

	assert(info);

	for (i = 0; i < info->num_output_formats; i++) {
		if (info->output_formats[i] == format)
			return true;
	}
	return false;
}

static bool
binary_supports_vf_format(const struct ia_css_binary_xinfo *info,
			  enum ia_css_frame_format format)
{
	int i;

	assert(info);

	for (i = 0; i < info->num_vf_formats; i++) {
		if (info->vf_formats[i] == format)
			return true;
	}
	return false;
}

/* move to host part of bds module */
static bool
supports_bds_factor(u32 supported_factors,
		    uint32_t bds_factor)
{
	return ((supported_factors & PACK_BDS_FACTOR(bds_factor)) != 0);
}

static int
binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i,
		 bool *binary_found) {
	const unsigned char *blob = sh_css_blob_info[i].blob;
	unsigned int size = sh_css_blob_info[i].header.blob.size;

	if ((!info) || (!binary_found))
		return -EINVAL;

	*info = sh_css_blob_info[i].header.info.isp;
	*binary_found = blob;
	info->blob_index = i;
	/* we don't have this binary, skip it */
	if (!size)
		return 0;

	info->xmem_addr = sh_css_load_blob(blob, size);
	if (!info->xmem_addr)
		return -ENOMEM;
	return 0;
}

/* When binaries are put at the beginning, they will only
 * be selected if no other primary matches.
 */
int
ia_css_binary_init_infos(void) {
	unsigned int i;
	unsigned int num_of_isp_binaries = sh_css_num_binaries - NUM_OF_SPS - NUM_OF_BLS;

	if (num_of_isp_binaries == 0)
		return 0;

	all_binaries = kvmalloc(num_of_isp_binaries * sizeof(*all_binaries),
				GFP_KERNEL);
	if (!all_binaries)
		return -ENOMEM;

	for (i = 0; i < num_of_isp_binaries; i++)
	{
		int ret;
		struct ia_css_binary_xinfo *binary = &all_binaries[i];
		bool binary_found;

		ret = binary_init_info(binary, i, &binary_found);
		if (ret)
			return ret;
		if (!binary_found)
			continue;
		/* Prepend new binary information */
		binary->next = binary_infos[binary->sp.pipeline.mode];
		binary_infos[binary->sp.pipeline.mode] = binary;
		binary->blob = &sh_css_blob_info[i];
		binary->mem_offsets = sh_css_blob_info[i].mem_offsets;
	}
	return 0;
}

int
ia_css_binary_uninit(void) {
	unsigned int i;
	struct ia_css_binary_xinfo *b;

	for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++)
	{
		for (b = binary_infos[i]; b; b = b->next) {
			if (b->xmem_addr)
				hmm_free(b->xmem_addr);
			b->xmem_addr = mmgr_NULL;
		}
		binary_infos[i] = NULL;
	}
	kvfree(all_binaries);
	return 0;
}

/* @brief Compute decimation factor for 3A statistics and shading correction.
 *
 * @param[in]	width	Frame width in pixels.
 * @param[in]	height	Frame height in pixels.
 * @return	Log2 of decimation factor (= grid cell size) in bayer quads.
 */
static int
binary_grid_deci_factor_log2(int width, int height)
{
	/* 3A/Shading decimation factor spcification (at August 2008)
	 * ------------------------------------------------------------------
	 * [Image Width (BQ)] [Decimation Factor (BQ)] [Resulting grid cells]
	#ifndef ISP2401
	 * 1280 ?c             32                       40 ?c
	 *  640 ?c 1279        16                       40 ?c 80
	 *      ?c  639         8                          ?c 80
	#else
	 * from 1280                   32                 from 40
	 * from  640 to 1279           16                 from 40 to 80
	 *           to  639            8                         to 80
	#endif
	 * ------------------------------------------------------------------
	 */
	/* Maximum and minimum decimation factor by the specification */
#define MAX_SPEC_DECI_FACT_LOG2		5
#define MIN_SPEC_DECI_FACT_LOG2		3
	/* the smallest frame width in bayer quads when decimation factor (log2) is 5 or 4, by the specification */
#define DECI_FACT_LOG2_5_SMALLEST_FRAME_WIDTH_BQ	1280
#define DECI_FACT_LOG2_4_SMALLEST_FRAME_WIDTH_BQ	640

	int smallest_factor; /* the smallest factor (log2) where the number of cells does not exceed the limitation */
	int spec_factor;     /* the factor (log2) which satisfies the specification */

	/* Currently supported maximum width and height are 5120(=80*64) and 3840(=60*64). */
	assert(ISP_BQ_GRID_WIDTH(width,
				 MAX_SPEC_DECI_FACT_LOG2) <= SH_CSS_MAX_BQ_GRID_WIDTH);
	assert(ISP_BQ_GRID_HEIGHT(height,
				  MAX_SPEC_DECI_FACT_LOG2) <= SH_CSS_MAX_BQ_GRID_HEIGHT);

	/* Compute the smallest factor. */
	smallest_factor = MAX_SPEC_DECI_FACT_LOG2;
	while (ISP_BQ_GRID_WIDTH(width,
				 smallest_factor - 1) <= SH_CSS_MAX_BQ_GRID_WIDTH &&
	       ISP_BQ_GRID_HEIGHT(height, smallest_factor - 1) <= SH_CSS_MAX_BQ_GRID_HEIGHT
	       && smallest_factor > MIN_SPEC_DECI_FACT_LOG2)
		smallest_factor--;

	/* Get the factor by the specification. */
	if (_ISP_BQS(width) >= DECI_FACT_LOG2_5_SMALLEST_FRAME_WIDTH_BQ)
		spec_factor = 5;
	else if (_ISP_BQS(width) >= DECI_FACT_LOG2_4_SMALLEST_FRAME_WIDTH_BQ)
		spec_factor = 4;
	else
		spec_factor = 3;

	/* If smallest_factor is smaller than or equal to spec_factor, choose spec_factor to follow the specification.
	   If smallest_factor is larger than spec_factor, choose smallest_factor.

		ex. width=2560, height=1920
			smallest_factor=4, spec_factor=5
			smallest_factor < spec_factor   ->   return spec_factor

		ex. width=300, height=3000
			smallest_factor=5, spec_factor=3
			smallest_factor > spec_factor   ->   return smallest_factor
	*/
	return max(smallest_factor, spec_factor);

#undef MAX_SPEC_DECI_FACT_LOG2
#undef MIN_SPEC_DECI_FACT_LOG2
#undef DECI_FACT_LOG2_5_SMALLEST_FRAME_WIDTH_BQ
#undef DECI_FACT_LOG2_4_SMALLEST_FRAME_WIDTH_BQ
}

static int
binary_in_frame_padded_width(int in_frame_width,
			     int isp_internal_width,
			     int dvs_env_width,
			     int stream_config_left_padding,
			     int left_cropping,
			     bool need_scaling)
{
	int rval;
	int nr_of_left_paddings;	/* number of paddings pixels on the left of an image line */

#if defined(ISP2401)
	/* the output image line of Input System 2401 does not have the left paddings  */
	nr_of_left_paddings = 0;
#else
	/* in other cases, the left padding pixels are always 128 */
	nr_of_left_paddings = 2 * ISP_VEC_NELEMS;
#endif
	if (need_scaling) {
		/* In SDV use-case, we need to match left-padding of
		 * primary and the video binary. */
		if (stream_config_left_padding != -1) {
			/* Different than before, we do left&right padding. */
			rval =
			    CEIL_MUL(in_frame_width + nr_of_left_paddings,
				     2 * ISP_VEC_NELEMS);
		} else {
			/* Different than before, we do left&right padding. */
			in_frame_width += dvs_env_width;
			rval =
			    CEIL_MUL(in_frame_width +
				     (left_cropping ? nr_of_left_paddings : 0),
				     2 * ISP_VEC_NELEMS);
		}
	} else {
		rval = isp_internal_width;
	}

	return rval;
}

int
ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
			bool online,
			bool two_ppc,
			enum atomisp_input_format stream_format,
			const struct ia_css_frame_info *in_info, /* can be NULL */
			const struct ia_css_frame_info *bds_out_info, /* can be NULL */
			const struct ia_css_frame_info *out_info[], /* can be NULL */
			const struct ia_css_frame_info *vf_info, /* can be NULL */
			struct ia_css_binary *binary,
			struct ia_css_resolution *dvs_env,
			int stream_config_left_padding,
			bool accelerator) {
	const struct ia_css_binary_info *info = &xinfo->sp;
	unsigned int dvs_env_width = 0,
	dvs_env_height = 0,
	vf_log_ds = 0,
	s3a_log_deci = 0,
	bits_per_pixel = 0,
	/* Resolution at SC/3A/DIS kernel. */
	sc_3a_dis_width = 0,
	/* Resolution at SC/3A/DIS kernel. */
	sc_3a_dis_padded_width = 0,
	/* Resolution at SC/3A/DIS kernel. */
	sc_3a_dis_height = 0,
	isp_internal_width = 0,
	isp_internal_height = 0,
	s3a_isp_width = 0;

	bool need_scaling = false;
	struct ia_css_resolution binary_dvs_env, internal_res;
	int err;
	unsigned int i;
	const struct ia_css_frame_info *bin_out_info = NULL;

	assert(info);
	assert(binary);

	binary->info = xinfo;
	if (!accelerator)
	{
		/* binary->css_params has been filled by accelerator itself. */
		err = ia_css_isp_param_allocate_isp_parameters(
		    &binary->mem_params, &binary->css_params,
		    &info->mem_initializers);
		if (err) {
			return err;
		}
	}
	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
	{
		if (out_info[i] && (out_info[i]->res.width != 0)) {
			bin_out_info = out_info[i];
			break;
		}
	}
	if (in_info && bin_out_info)
	{
		need_scaling = (in_info->res.width != bin_out_info->res.width) ||
			       (in_info->res.height != bin_out_info->res.height);
	}

	/* binary_dvs_env has to be equal or larger than SH_CSS_MIN_DVS_ENVELOPE */
	binary_dvs_env.width = 0;
	binary_dvs_env.height = 0;
	ia_css_binary_dvs_env(info, dvs_env, &binary_dvs_env);
	dvs_env_width = binary_dvs_env.width;
	dvs_env_height = binary_dvs_env.height;
	binary->dvs_envelope.width  = dvs_env_width;
	binary->dvs_envelope.height = dvs_env_height;

	/* internal resolution calculation */
	internal_res.width = 0;
	internal_res.height = 0;
	ia_css_binary_internal_res(in_info, bds_out_info, bin_out_info, dvs_env,
				   info, &internal_res);
	isp_internal_width = internal_res.width;
	isp_internal_height = internal_res.height;

	/* internal frame info */
	if (bin_out_info) /* { */
		binary->internal_frame_info.format = bin_out_info->format;
	/* } */
	binary->internal_frame_info.res.width       = isp_internal_width;
	binary->internal_frame_info.padded_width    = CEIL_MUL(isp_internal_width, 2 * ISP_VEC_NELEMS);
	binary->internal_frame_info.res.height      = isp_internal_height;
	binary->internal_frame_info.raw_bit_depth   = bits_per_pixel;

	if (in_info)
	{
		binary->effective_in_frame_res.width = in_info->res.width;
		binary->effective_in_frame_res.height = in_info->res.height;

		bits_per_pixel = in_info->raw_bit_depth;

		/* input info */
		binary->in_frame_info.res.width = in_info->res.width +
						  info->pipeline.left_cropping;
		binary->in_frame_info.res.height = in_info->res.height +
						   info->pipeline.top_cropping;

		binary->in_frame_info.res.width += dvs_env_width;
		binary->in_frame_info.res.height += dvs_env_height;

		binary->in_frame_info.padded_width =
		    binary_in_frame_padded_width(in_info->res.width,
						 isp_internal_width,
						 dvs_env_width,
						 stream_config_left_padding,
						 info->pipeline.left_cropping,
						 need_scaling);

		binary->in_frame_info.format = in_info->format;
		binary->in_frame_info.raw_bayer_order = in_info->raw_bayer_order;
		binary->in_frame_info.crop_info = in_info->crop_info;
	}

	if (online)
	{
		bits_per_pixel = ia_css_util_input_format_bpp(
				     stream_format, two_ppc);
	}
	binary->in_frame_info.raw_bit_depth = bits_per_pixel;

	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
	{
		if (out_info[i]) {
			binary->out_frame_info[i].res.width     = out_info[i]->res.width;
			binary->out_frame_info[i].res.height    = out_info[i]->res.height;
			binary->out_frame_info[i].padded_width  = out_info[i]->padded_width;
			if (info->pipeline.mode == IA_CSS_BINARY_MODE_COPY) {
				binary->out_frame_info[i].raw_bit_depth = bits_per_pixel;
			} else {
				/* Only relevant for RAW format.
				 * At the moment, all outputs are raw, 16 bit per pixel, except for copy.
				 * To do this cleanly, the binary should specify in its info
				 * the bit depth per output channel.
				 */
				binary->out_frame_info[i].raw_bit_depth = 16;
			}
			binary->out_frame_info[i].format        = out_info[i]->format;
		}
	}

	if (vf_info && (vf_info->res.width != 0))
	{
		err = ia_css_vf_configure(binary, bin_out_info,
					  (struct ia_css_frame_info *)vf_info, &vf_log_ds);
		if (err) {
			if (!accelerator) {
				ia_css_isp_param_destroy_isp_parameters(
				    &binary->mem_params,
				    &binary->css_params);
			}
			return err;
		}
	}
	binary->vf_downscale_log2 = vf_log_ds;

	binary->online            = online;
	binary->input_format      = stream_format;

	/* viewfinder output info */
	if ((vf_info) && (vf_info->res.width != 0))
	{
		unsigned int vf_out_vecs, vf_out_width, vf_out_height;

		binary->vf_frame_info.format = vf_info->format;
		if (!bin_out_info)
			return -EINVAL;
		vf_out_vecs = __ISP_VF_OUTPUT_WIDTH_VECS(bin_out_info->padded_width,
			      vf_log_ds);
		vf_out_width = _ISP_VF_OUTPUT_WIDTH(vf_out_vecs);
		vf_out_height = _ISP_VF_OUTPUT_HEIGHT(bin_out_info->res.height,
						      vf_log_ds);

		/* For preview mode, output pin is used instead of vf. */
		if (info->pipeline.mode == IA_CSS_BINARY_MODE_PREVIEW) {
			binary->out_frame_info[0].res.width =
			    (bin_out_info->res.width >> vf_log_ds);
			binary->out_frame_info[0].padded_width = vf_out_width;
			binary->out_frame_info[0].res.height   = vf_out_height;

			binary->vf_frame_info.res.width    = 0;
			binary->vf_frame_info.padded_width = 0;
			binary->vf_frame_info.res.height   = 0;
		} else {
			/* we also store the raw downscaled width. This is
			 * used for digital zoom in preview to zoom only on
			 * the width that we actually want to keep, not on
			 * the aligned width. */
			binary->vf_frame_info.res.width =
			    (bin_out_info->res.width >> vf_log_ds);
			binary->vf_frame_info.padded_width = vf_out_width;
			binary->vf_frame_info.res.height   = vf_out_height;
		}
	} else
	{
		binary->vf_frame_info.res.width    = 0;
		binary->vf_frame_info.padded_width = 0;
		binary->vf_frame_info.res.height   = 0;
	}

	if (info->enable.ca_gdc)
	{
		binary->morph_tbl_width =
		    _ISP_MORPH_TABLE_WIDTH(isp_internal_width);
		binary->morph_tbl_aligned_width  =
		    _ISP_MORPH_TABLE_ALIGNED_WIDTH(isp_internal_width);
		binary->morph_tbl_height =
		    _ISP_MORPH_TABLE_HEIGHT(isp_internal_height);
	} else
	{
		binary->morph_tbl_width  = 0;
		binary->morph_tbl_aligned_width  = 0;
		binary->morph_tbl_height = 0;
	}

	sc_3a_dis_width = binary->in_frame_info.res.width;
	sc_3a_dis_padded_width = binary->in_frame_info.padded_width;
	sc_3a_dis_height = binary->in_frame_info.res.height;
	if (bds_out_info && in_info &&
	    bds_out_info->res.width != in_info->res.width)
	{
		/* TODO: Next, "internal_frame_info" should be derived from
		 * bds_out. So this part will change once it is in place! */
		sc_3a_dis_width = bds_out_info->res.width + info->pipeline.left_cropping;
		sc_3a_dis_padded_width = isp_internal_width;
		sc_3a_dis_height = isp_internal_height;
	}

	s3a_isp_width = _ISP_S3A_ELEMS_ISP_WIDTH(sc_3a_dis_padded_width,
			info->pipeline.left_cropping);
	if (info->s3a.fixed_s3a_deci_log)
	{
		s3a_log_deci = info->s3a.fixed_s3a_deci_log;
	} else
	{
		s3a_log_deci = binary_grid_deci_factor_log2(s3a_isp_width,
			       sc_3a_dis_height);
	}
	binary->deci_factor_log2  = s3a_log_deci;

	if (info->enable.s3a)
	{
		binary->s3atbl_width  =
		    _ISP_S3ATBL_WIDTH(sc_3a_dis_width,
				      s3a_log_deci);
		binary->s3atbl_height =
		    _ISP_S3ATBL_HEIGHT(sc_3a_dis_height,
				       s3a_log_deci);
		binary->s3atbl_isp_width =
		    _ISP_S3ATBL_ISP_WIDTH(s3a_isp_width,
					  s3a_log_deci);
		binary->s3atbl_isp_height =
		    _ISP_S3ATBL_ISP_HEIGHT(sc_3a_dis_height,
					   s3a_log_deci);
	} else
	{
		binary->s3atbl_width  = 0;
		binary->s3atbl_height = 0;
		binary->s3atbl_isp_width  = 0;
		binary->s3atbl_isp_height = 0;
	}

	if (info->enable.sc)
	{
		if (!IS_ISP2401) {
			binary->sctbl_width_per_color = _ISP2400_SCTBL_WIDTH_PER_COLOR(sc_3a_dis_padded_width, s3a_log_deci);
			binary->sctbl_aligned_width_per_color = ISP2400_SH_CSS_MAX_SCTBL_ALIGNED_WIDTH_PER_COLOR;
			binary->sctbl_height = _ISP2400_SCTBL_HEIGHT(sc_3a_dis_height, s3a_log_deci);
		} else {
			binary->sctbl_width_per_color = _ISP2401_SCTBL_WIDTH_PER_COLOR(isp_internal_width, s3a_log_deci);
			binary->sctbl_aligned_width_per_color = ISP2401_SH_CSS_MAX_SCTBL_ALIGNED_WIDTH_PER_COLOR;
			binary->sctbl_height = _ISP2401_SCTBL_HEIGHT(isp_internal_height, s3a_log_deci);
			binary->sctbl_legacy_width_per_color  = _ISP_SCTBL_LEGACY_WIDTH_PER_COLOR(sc_3a_dis_padded_width, s3a_log_deci);
			binary->sctbl_legacy_height = _ISP_SCTBL_LEGACY_HEIGHT(sc_3a_dis_height, s3a_log_deci);
		}
	} else
	{
		binary->sctbl_width_per_color         = 0;
		binary->sctbl_aligned_width_per_color = 0;
		binary->sctbl_height                  = 0;
		if (IS_ISP2401) {
			binary->sctbl_legacy_width_per_color  = 0;
			binary->sctbl_legacy_height	      = 0;
		}
	}
	ia_css_sdis_init_info(&binary->dis,
			      sc_3a_dis_width,
			      sc_3a_dis_padded_width,
			      sc_3a_dis_height,
			      info->pipeline.isp_pipe_version,
			      info->enable.dis);
	if (info->pipeline.left_cropping)
		binary->left_padding = 2 * ISP_VEC_NELEMS - info->pipeline.left_cropping;
	else
		binary->left_padding = 0;

	return 0;
}

static int __ia_css_binary_find(struct ia_css_binary_descr *descr,
				struct ia_css_binary *binary) {
	int mode;
	bool online;
	bool two_ppc;
	enum atomisp_input_format stream_format;
	const struct ia_css_frame_info *req_in_info,
		*req_bds_out_info,
		*req_out_info[IA_CSS_BINARY_MAX_OUTPUT_PORTS],
		*req_bin_out_info = NULL,
		*req_vf_info;

	struct ia_css_binary_xinfo *xcandidate;
#ifndef ISP2401
	bool need_ds, need_dz, need_dvs, need_xnr, need_dpc;
#else
	bool need_ds, need_dz, need_dvs, need_xnr, need_dpc, need_tnr;
#endif
	bool striped;
	bool enable_yuv_ds;
	bool enable_high_speed;
	bool enable_dvs_6axis;
	bool enable_reduced_pipe;
	bool enable_capture_pp_bli;
#ifdef ISP2401
	bool enable_luma_only;
#endif
	int err = -EINVAL;
	bool continuous;
	unsigned int isp_pipe_version;
	struct ia_css_resolution dvs_env, internal_res;
	unsigned int i;

	assert(descr);
	/* MW: used after an error check, may accept NULL, but doubtfull */
	assert(binary);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "ia_css_binary_find() enter: descr=%p, (mode=%d), binary=%p\n",
			    descr, descr->mode,
			    binary);

	mode = descr->mode;
	online = descr->online;
	two_ppc = descr->two_ppc;
	stream_format = descr->stream_format;
	req_in_info = descr->in_info;
	req_bds_out_info = descr->bds_out_info;
	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
	{
		req_out_info[i] = descr->out_info[i];
		if (req_out_info[i] && (req_out_info[i]->res.width != 0))
			req_bin_out_info = req_out_info[i];
	}
	if (!req_bin_out_info)
		return -EINVAL;
#ifndef ISP2401
	req_vf_info = descr->vf_info;
#else

	if ((descr->vf_info) && (descr->vf_info->res.width == 0))
		/* width==0 means that there is no vf pin (e.g. in SkyCam preview case) */
		req_vf_info = NULL;
	else
		req_vf_info = descr->vf_info;
#endif

	need_xnr = descr->enable_xnr;
	need_ds = descr->enable_fractional_ds;
	need_dz = false;
	need_dvs = false;
	need_dpc = descr->enable_dpc;
#ifdef ISP2401
	need_tnr = descr->enable_tnr;
#endif
	enable_yuv_ds = descr->enable_yuv_ds;
	enable_high_speed = descr->enable_high_speed;
	enable_dvs_6axis  = descr->enable_dvs_6axis;
	enable_reduced_pipe = descr->enable_reduced_pipe;
	enable_capture_pp_bli = descr->enable_capture_pp_bli;
#ifdef ISP2401
	enable_luma_only = descr->enable_luma_only;
#endif
	continuous = descr->continuous;
	striped = descr->striped;
	isp_pipe_version = descr->isp_pipe_version;

	dvs_env.width = 0;
	dvs_env.height = 0;
	internal_res.width = 0;
	internal_res.height = 0;

	if (mode == IA_CSS_BINARY_MODE_VIDEO)
	{
		dvs_env = descr->dvs_env;
		need_dz = descr->enable_dz;
		/* Video is the only mode that has a nodz variant. */
		need_dvs = dvs_env.width || dvs_env.height;
	}

	/* print a map of the binary file */
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,	"BINARY INFO:\n");
	for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++)
	{
		xcandidate = binary_infos[i];
		if (xcandidate) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,	"%d:\n", i);
			while (xcandidate) {
				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, " Name:%s Type:%d Cont:%d\n",
						    xcandidate->blob->name, xcandidate->type,
						    xcandidate->sp.enable.continuous);
				xcandidate = xcandidate->next;
			}
		}
	}

	/* printf("sh_css_binary_find: pipe version %d\n", isp_pipe_version); */
	for (xcandidate = binary_infos[mode]; xcandidate;
	     xcandidate = xcandidate->next)
	{
		struct ia_css_binary_info *candidate = &xcandidate->sp;
		/* printf("sh_css_binary_find: evaluating candidate:
		 * %d\n",candidate->id); */
		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
				    "ia_css_binary_find() candidate = %p, mode = %d ID = %d\n",
				    candidate, candidate->pipeline.mode, candidate->id);

		/*
		 * MW: Only a limited set of jointly configured binaries can
		 * be used in a continuous preview/video mode unless it is
		 * the copy mode and runs on SP.
		*/
		if (!candidate->enable.continuous &&
		    continuous && (mode != IA_CSS_BINARY_MODE_COPY)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d && (%d != %d)\n",
					    __LINE__, candidate->enable.continuous,
					    continuous, mode,
					    IA_CSS_BINARY_MODE_COPY);
			continue;
		}
		if (striped && candidate->iterator.num_stripes == 1) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: binary is not striped\n",
					    __LINE__);
			continue;
		}

		if (candidate->pipeline.isp_pipe_version != isp_pipe_version &&
		    (mode != IA_CSS_BINARY_MODE_COPY) &&
		    (mode != IA_CSS_BINARY_MODE_CAPTURE_PP) &&
		    (mode != IA_CSS_BINARY_MODE_VF_PP)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d != %d)\n",
					    __LINE__,
					    candidate->pipeline.isp_pipe_version, isp_pipe_version);
			continue;
		}
		if (!candidate->enable.reduced_pipe && enable_reduced_pipe) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d\n",
					    __LINE__,
					    candidate->enable.reduced_pipe,
					    enable_reduced_pipe);
			continue;
		}
		if (!candidate->enable.dvs_6axis && enable_dvs_6axis) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d\n",
					    __LINE__,
					    candidate->enable.dvs_6axis,
					    enable_dvs_6axis);
			continue;
		}
		if (candidate->enable.high_speed && !enable_high_speed) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: %d && !%d\n",
					    __LINE__,
					    candidate->enable.high_speed,
					    enable_high_speed);
			continue;
		}
		if (!candidate->enable.xnr && need_xnr) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: %d && !%d\n",
					    __LINE__,
					    candidate->enable.xnr,
					    need_xnr);
			continue;
		}
		if (!(candidate->enable.ds & 2) && enable_yuv_ds) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d\n",
					    __LINE__,
					    ((candidate->enable.ds & 2) != 0),
					    enable_yuv_ds);
			continue;
		}
		if ((candidate->enable.ds & 2) && !enable_yuv_ds) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: %d && !%d\n",
					    __LINE__,
					    ((candidate->enable.ds & 2) != 0),
					    enable_yuv_ds);
			continue;
		}

		if (mode == IA_CSS_BINARY_MODE_VIDEO &&
		    candidate->enable.ds && need_ds)
			need_dz = false;

		/* when we require vf output, we need to have vf_veceven */
		if ((req_vf_info) && !(candidate->enable.vf_veceven ||
				       /* or variable vf vec even */
				       candidate->vf_dec.is_variable ||
				       /* or more than one output pin. */
				       xcandidate->num_output_pins > 1)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%p != NULL) && !(%d || %d || (%d >%d))\n",
					    __LINE__, req_vf_info,
					    candidate->enable.vf_veceven,
					    candidate->vf_dec.is_variable,
					    xcandidate->num_output_pins, 1);
			continue;
		}
		if (!candidate->enable.dvs_envelope && need_dvs) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d\n",
					    __LINE__,
					    candidate->enable.dvs_envelope, (int)need_dvs);
			continue;
		}
		/* internal_res check considers input, output, and dvs envelope sizes */
		ia_css_binary_internal_res(req_in_info, req_bds_out_info,
					   req_bin_out_info, &dvs_env, candidate, &internal_res);
		if (internal_res.width > candidate->internal.max_width) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d > %d)\n",
					    __LINE__, internal_res.width,
					    candidate->internal.max_width);
			continue;
		}
		if (internal_res.height > candidate->internal.max_height) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d > %d)\n",
					    __LINE__, internal_res.height,
					    candidate->internal.max_height);
			continue;
		}
		if (!candidate->enable.ds && need_ds && !(xcandidate->num_output_pins > 1)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d\n",
					    __LINE__, candidate->enable.ds, (int)need_ds);
			continue;
		}
		if (!candidate->enable.uds && !candidate->enable.dvs_6axis && need_dz) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && !%d && %d\n",
					    __LINE__, candidate->enable.uds,
					    candidate->enable.dvs_6axis, (int)need_dz);
			continue;
		}
		if (online && candidate->input.source == IA_CSS_BINARY_INPUT_MEMORY) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: %d && (%d == %d)\n",
					    __LINE__, online, candidate->input.source,
					    IA_CSS_BINARY_INPUT_MEMORY);
			continue;
		}
		if (!online && candidate->input.source == IA_CSS_BINARY_INPUT_SENSOR) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && (%d == %d)\n",
					    __LINE__, online, candidate->input.source,
					    IA_CSS_BINARY_INPUT_SENSOR);
			continue;
		}
		if (req_bin_out_info->res.width < candidate->output.min_width ||
		    req_bin_out_info->res.width > candidate->output.max_width) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d > %d) || (%d < %d)\n",
					    __LINE__,
					    req_bin_out_info->padded_width,
					    candidate->output.min_width,
					    req_bin_out_info->padded_width,
					    candidate->output.max_width);
			continue;
		}
		if (xcandidate->num_output_pins > 1 &&
		    /* in case we have a second output pin, */
		    req_vf_info) { /* and we need vf output. */
			if (req_vf_info->res.width > candidate->output.max_width) {
				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
						    "ia_css_binary_find() [%d] continue: (%d < %d)\n",
						    __LINE__,
						    req_vf_info->res.width,
						    candidate->output.max_width);
				continue;
			}
		}
		if (req_in_info->padded_width > candidate->input.max_width) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d > %d)\n",
					    __LINE__, req_in_info->padded_width,
					    candidate->input.max_width);
			continue;
		}
		if (!binary_supports_output_format(xcandidate, req_bin_out_info->format)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d\n",
					    __LINE__,
					    binary_supports_output_format(xcandidate, req_bin_out_info->format));
			continue;
		}
		if (xcandidate->num_output_pins > 1 &&
		    /* in case we have a second output pin, */
		    req_vf_info                   && /* and we need vf output. */
		    /* check if the required vf format
		    is supported. */
		    !binary_supports_output_format(xcandidate, req_vf_info->format)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d > %d) && (%p != NULL) && !%d\n",
					    __LINE__, xcandidate->num_output_pins, 1,
					    req_vf_info,
					    binary_supports_output_format(xcandidate, req_vf_info->format));
			continue;
		}

		/* Check if vf_veceven supports the requested vf format */
		if (xcandidate->num_output_pins == 1 &&
		    req_vf_info && candidate->enable.vf_veceven &&
		    !binary_supports_vf_format(xcandidate, req_vf_info->format)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: (%d == %d) && (%p != NULL) && %d && !%d\n",
					    __LINE__, xcandidate->num_output_pins, 1,
					    req_vf_info, candidate->enable.vf_veceven,
					    binary_supports_vf_format(xcandidate, req_vf_info->format));
			continue;
		}

		/* Check if vf_veceven supports the requested vf width */
		if (xcandidate->num_output_pins == 1 &&
		    req_vf_info && candidate->enable.vf_veceven) { /* and we need vf output. */
			if (req_vf_info->res.width > candidate->output.max_width) {
				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
						    "ia_css_binary_find() [%d] continue: (%d < %d)\n",
						    __LINE__,
						    req_vf_info->res.width,
						    candidate->output.max_width);
				continue;
			}
		}

		if (!supports_bds_factor(candidate->bds.supported_bds_factors,
					 descr->required_bds_factor)) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n",
					    __LINE__, candidate->bds.supported_bds_factors,
					    descr->required_bds_factor);
			continue;
		}

		if (!candidate->enable.dpc && need_dpc) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n",
					    __LINE__, candidate->enable.dpc,
					    descr->enable_dpc);
			continue;
		}

		if (candidate->uds.use_bci && enable_capture_pp_bli) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n",
					    __LINE__, candidate->uds.use_bci,
					    descr->enable_capture_pp_bli);
			continue;
		}

#ifdef ISP2401
		if (candidate->enable.luma_only != enable_luma_only) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: %d != %d\n",
					    __LINE__, candidate->enable.luma_only,
					    descr->enable_luma_only);
			continue;
		}

		if (!candidate->enable.tnr && need_tnr) {
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "ia_css_binary_find() [%d] continue: !%d && %d\n",
					    __LINE__, candidate->enable.tnr,
					    descr->enable_tnr);
			continue;
		}

#endif
		/* reconfigure any variable properties of the binary */
		err = ia_css_binary_fill_info(xcandidate, online, two_ppc,
					      stream_format, req_in_info,
					      req_bds_out_info,
					      req_out_info, req_vf_info,
					      binary, &dvs_env,
					      descr->stream_config_left_padding,
					      false);

		if (err)
			break;
		binary_init_metrics(&binary->metrics, &binary->info->sp);
		break;
	}

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "ia_css_binary_find() selected = %p, mode = %d ID = %d\n",
			    xcandidate, xcandidate ? xcandidate->sp.pipeline.mode : 0, xcandidate ? xcandidate->sp.id : 0);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "ia_css_binary_find() leave: return_err=%d\n", err);

	if (!err && xcandidate)
		dev_dbg(atomisp_dev,
			"Using binary %s (id %d), type %d, mode %d, continuous %s\n",
			xcandidate->blob->name,
			xcandidate->sp.id,
			xcandidate->type,
			xcandidate->sp.pipeline.mode,
			xcandidate->sp.enable.continuous ? "true" : "false");


	return err;
}

int ia_css_binary_find(struct ia_css_binary_descr *descr,
		       struct ia_css_binary *binary)
{
	int ret = __ia_css_binary_find(descr, binary);

	if (unlikely(ret)) {
		dev_dbg(atomisp_dev, "Seeking for binary failed at:");
		dump_stack();
	}

	return ret;
}

unsigned
ia_css_binary_max_vf_width(void)
{
	/* This is (should be) true for IPU1 and IPU2 */
	/* For IPU3 (SkyCam) this pointer is guaranteed to be NULL simply because such a binary does not exist  */
	if (binary_infos[IA_CSS_BINARY_MODE_VF_PP])
		return binary_infos[IA_CSS_BINARY_MODE_VF_PP]->sp.output.max_width;
	return 0;
}

void
ia_css_binary_destroy_isp_parameters(struct ia_css_binary *binary)
{
	if (binary) {
		ia_css_isp_param_destroy_isp_parameters(&binary->mem_params,
							&binary->css_params);
	}
}

void
ia_css_binary_get_isp_binaries(struct ia_css_binary_xinfo **binaries,
			       uint32_t *num_isp_binaries)
{
	assert(binaries);

	if (num_isp_binaries)
		*num_isp_binaries = 0;

	*binaries = all_binaries;
	if (all_binaries && num_isp_binaries) {
		/* -1 to account for sp binary which is not stored in all_binaries */
		if (sh_css_num_binaries > 0)
			*num_isp_binaries = sh_css_num_binaries - 1;
	}
}
