// 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.
 */

/*! \file */
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

#include "hmm.h"

#include "ia_css.h"
#include "sh_css_hrt.h"		/* only for file 2 MIPI */
#include "ia_css_buffer.h"
#include "ia_css_binary.h"
#include "sh_css_internal.h"
#include "sh_css_mipi.h"
#include "sh_css_sp.h"		/* sh_css_sp_group */
#if !defined(HAS_NO_INPUT_SYSTEM)
#include "ia_css_isys.h"
#endif
#include "ia_css_frame.h"
#include "sh_css_defs.h"
#include "sh_css_firmware.h"
#include "sh_css_params.h"
#include "sh_css_params_internal.h"
#include "sh_css_param_shading.h"
#include "ia_css_refcount.h"
#include "ia_css_rmgr.h"
#include "ia_css_debug.h"
#include "ia_css_debug_pipe.h"
#include "ia_css_device_access.h"
#include "device_access.h"
#include "sh_css_legacy.h"
#include "ia_css_pipeline.h"
#include "ia_css_stream.h"
#include "sh_css_stream_format.h"
#include "ia_css_pipe.h"
#include "ia_css_util.h"
#include "ia_css_pipe_util.h"
#include "ia_css_pipe_binarydesc.h"
#include "ia_css_pipe_stagedesc.h"
#ifdef USE_INPUT_SYSTEM_VERSION_2
#include "ia_css_isys.h"
#endif

#include "tag.h"
#include "assert_support.h"
#include "math_support.h"
#include "sw_event_global.h"			/* Event IDs.*/
#if !defined(HAS_NO_INPUT_FORMATTER)
#include "ia_css_ifmtr.h"
#endif
#if !defined(HAS_NO_INPUT_SYSTEM)
#include "input_system.h"
#endif
#include "mmu_device.h"		/* mmu_set_page_table_base_index(), ... */
#include "ia_css_mmu_private.h" /* sh_css_mmu_set_page_table_base_index() */
#include "gdc_device.h"		/* HRT_GDC_N */
#include "dma.h"		/* dma_set_max_burst_size() */
#include "irq.h"			/* virq */
#include "sp.h"				/* cnd_sp_irq_enable() */
#include "isp.h"			/* cnd_isp_irq_enable, ISP_VEC_NELEMS */
#include "gp_device.h"		/* gp_device_reg_store() */
#define __INLINE_GPIO__
#include "gpio.h"
#include "timed_ctrl.h"
#include "ia_css_inputfifo.h"
#define WITH_PC_MONITORING  0

#define SH_CSS_VIDEO_BUFFER_ALIGNMENT 0

#if WITH_PC_MONITORING
#define MULTIPLE_SAMPLES 1
#define NOF_SAMPLES      60
#include "linux/kthread.h"
#include "linux/sched.h"
#include "linux/delay.h"
#include "sh_css_metrics.h"
static int thread_alive;
#endif /* WITH_PC_MONITORING */

#include "ia_css_spctrl.h"
#include "ia_css_version_data.h"
#include "sh_css_struct.h"
#include "ia_css_bufq.h"
#include "ia_css_timer.h" /* clock_value_t */

#include "isp/modes/interface/input_buf.isp.h"

/* Name of the sp program: should not be built-in */
#define SP_PROG_NAME "sp"
/* Size of Refcount List */
#define REFCOUNT_SIZE 1000

/* for JPEG, we don't know the length of the image upfront,
 * but since we support sensor upto 16MP, we take this as
 * upper limit.
 */
#define JPEG_BYTES (16 * 1024 * 1024)

#define STATS_ENABLED(stage) (stage && stage->binary && stage->binary->info && \
	(stage->binary->info->sp.enable.s3a || stage->binary->info->sp.enable.dis))

struct sh_css my_css;

int (*sh_css_printf)(const char *fmt, va_list args) = NULL;

/* modes of work: stream_create and stream_destroy will update the save/restore data
   only when in working mode, not suspend/resume
*/
enum ia_sh_css_modes {
	sh_css_mode_none = 0,
	sh_css_mode_working,
	sh_css_mode_suspend,
	sh_css_mode_resume
};

/* a stream seed, to save and restore the stream data.
   the stream seed contains all the data required to "grow" the seed again after it was closed.
*/
struct sh_css_stream_seed {
	struct ia_css_stream
		**orig_stream;                /* pointer to restore the original handle */
	struct ia_css_stream		*stream;                      /* handle, used as ID too.*/
	struct ia_css_stream_config	stream_config;				/* stream config struct */
	int				num_pipes;
	struct ia_css_pipe		*pipes[IA_CSS_PIPE_ID_NUM];			/* pipe handles */
	struct ia_css_pipe
		**orig_pipes[IA_CSS_PIPE_ID_NUM];	/* pointer to restore original handle */
	struct ia_css_pipe_config
		pipe_config[IA_CSS_PIPE_ID_NUM];	/* pipe config structs */
};

#define MAX_ACTIVE_STREAMS	5
/* A global struct for save/restore to hold all the data that should sustain power-down:
   MMU base, IRQ type, env for routines, binary loaded FW and the stream seeds.
*/
struct sh_css_save {
	enum ia_sh_css_modes		mode;
	u32		       mmu_base;				/* the last mmu_base */
	enum ia_css_irq_type           irq_type;
	struct sh_css_stream_seed      stream_seeds[MAX_ACTIVE_STREAMS];
	struct ia_css_fw	       *loaded_fw;				/* fw struct previously loaded */
	struct ia_css_env	       driver_env;				/* driver-supplied env copy */
};

static bool my_css_save_initialized;	/* if my_css_save was initialized */
static struct sh_css_save my_css_save;

/* pqiao NOTICE: this is for css internal buffer recycling when stopping pipeline,
   this array is temporary and will be replaced by resource manager*/
/* Taking the biggest Size for number of Elements */
#define MAX_HMM_BUFFER_NUM	\
	(SH_CSS_MAX_NUM_QUEUES * (IA_CSS_NUM_ELEMS_SP2HOST_BUFFER_QUEUE + 2))

struct sh_css_hmm_buffer_record {
	bool in_use;
	enum ia_css_buffer_type type;
	struct ia_css_rmgr_vbuf_handle *h_vbuf;
	hrt_address kernel_ptr;
};

static struct sh_css_hmm_buffer_record hmm_buffer_record[MAX_HMM_BUFFER_NUM];

#define GPIO_FLASH_PIN_MASK BIT(HIVE_GPIO_STROBE_TRIGGER_PIN)

static bool fw_explicitly_loaded;

/*
 * Local prototypes
 */

static int
allocate_delay_frames(struct ia_css_pipe *pipe);

static int
sh_css_pipe_start(struct ia_css_stream *stream);

/* ISP 2401 */
/*
 * @brief Stop all "ia_css_pipe" instances in the target
 * "ia_css_stream" instance.
 *
 * @param[in] stream	Point to the target "ia_css_stream" instance.
 *
 * @return
 * - 0, if the "stop" requests have been successfully sent out.
 * - CSS error code, otherwise.
 *
 *
 * NOTE
 * This API sends the "stop" requests to the "ia_css_pipe"
 * instances in the same "ia_css_stream" instance. It will
 * return without waiting for all "ia_css_pipe" instatnces
 * being stopped.
 */
static int
sh_css_pipes_stop(struct ia_css_stream *stream);

/*
 * @brief Check if all "ia_css_pipe" instances in the target
 * "ia_css_stream" instance have stopped.
 *
 * @param[in] stream	Point to the target "ia_css_stream" instance.
 *
 * @return
 * - true, if all "ia_css_pipe" instances in the target "ia_css_stream"
 *   instance have ben stopped.
 * - false, otherwise.
 */
/* ISP 2401 */
static bool
sh_css_pipes_have_stopped(struct ia_css_stream *stream);

/* ISP 2401 */
static int
ia_css_pipe_check_format(struct ia_css_pipe *pipe,
			 enum ia_css_frame_format format);

/* ISP 2401 */
static int
check_pipe_resolutions(const struct ia_css_pipe *pipe);

static int
ia_css_pipe_load_extension(struct ia_css_pipe *pipe,
			   struct ia_css_fw_info *firmware);

static void
ia_css_pipe_unload_extension(struct ia_css_pipe *pipe,
			     struct ia_css_fw_info *firmware);
static void
ia_css_reset_defaults(struct sh_css *css);

static void
sh_css_init_host_sp_control_vars(void);

static int set_num_primary_stages(unsigned int *num,
	enum ia_css_pipe_version version);

static bool
need_capture_pp(const struct ia_css_pipe *pipe);

static bool
need_yuv_scaler_stage(const struct ia_css_pipe *pipe);

static int ia_css_pipe_create_cas_scaler_desc_single_output(
    struct ia_css_frame_info *cas_scaler_in_info,
    struct ia_css_frame_info *cas_scaler_out_info,
    struct ia_css_frame_info *cas_scaler_vf_info,
    struct ia_css_cas_binary_descr *descr);

static void ia_css_pipe_destroy_cas_scaler_desc(struct ia_css_cas_binary_descr
	*descr);

static bool
need_downscaling(const struct ia_css_resolution in_res,
		 const struct ia_css_resolution out_res);

static bool need_capt_ldc(const struct ia_css_pipe *pipe);

static int
sh_css_pipe_load_binaries(struct ia_css_pipe *pipe);

static
int sh_css_pipe_get_viewfinder_frame_info(
    struct ia_css_pipe *pipe,
    struct ia_css_frame_info *info,
    unsigned int idx);

static int
sh_css_pipe_get_output_frame_info(struct ia_css_pipe *pipe,
				  struct ia_css_frame_info *info,
				  unsigned int idx);

static int
capture_start(struct ia_css_pipe *pipe);

static int
video_start(struct ia_css_pipe *pipe);

static int
preview_start(struct ia_css_pipe *pipe);

static int
yuvpp_start(struct ia_css_pipe *pipe);

static bool copy_on_sp(struct ia_css_pipe *pipe);

static int
init_vf_frameinfo_defaults(struct ia_css_pipe *pipe,
			   struct ia_css_frame *vf_frame, unsigned int idx);

static int
init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe,
				  struct ia_css_frame *frame, enum ia_css_frame_format format);

static int
init_out_frameinfo_defaults(struct ia_css_pipe *pipe,
			    struct ia_css_frame *out_frame, unsigned int idx);

static int
sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline,
			      const void *acc_fw);

static int
alloc_continuous_frames(
    struct ia_css_pipe *pipe, bool init_time);

static void
pipe_global_init(void);

static int
pipe_generate_pipe_num(const struct ia_css_pipe *pipe,
		       unsigned int *pipe_number);

static void
pipe_release_pipe_num(unsigned int pipe_num);

static int
create_host_pipeline_structure(struct ia_css_stream *stream);

static int
create_host_pipeline(struct ia_css_stream *stream);

static int
create_host_preview_pipeline(struct ia_css_pipe *pipe);

static int
create_host_video_pipeline(struct ia_css_pipe *pipe);

static int
create_host_copy_pipeline(struct ia_css_pipe *pipe,
			  unsigned int max_input_width,
			  struct ia_css_frame *out_frame);

static int
create_host_isyscopy_capture_pipeline(struct ia_css_pipe *pipe);

static int
create_host_capture_pipeline(struct ia_css_pipe *pipe);

static int
create_host_yuvpp_pipeline(struct ia_css_pipe *pipe);

static int
create_host_acc_pipeline(struct ia_css_pipe *pipe);

static unsigned int
sh_css_get_sw_interrupt_value(unsigned int irq);

static struct ia_css_binary *ia_css_pipe_get_shading_correction_binary(
    const struct ia_css_pipe *pipe);

static struct ia_css_binary *
ia_css_pipe_get_s3a_binary(const struct ia_css_pipe *pipe);

static struct ia_css_binary *
ia_css_pipe_get_sdis_binary(const struct ia_css_pipe *pipe);

static void
sh_css_hmm_buffer_record_init(void);

static void
sh_css_hmm_buffer_record_uninit(void);

static void
sh_css_hmm_buffer_record_reset(struct sh_css_hmm_buffer_record *buffer_record);

static struct sh_css_hmm_buffer_record
*sh_css_hmm_buffer_record_acquire(struct ia_css_rmgr_vbuf_handle *h_vbuf,
				  enum ia_css_buffer_type type,
				  hrt_address kernel_ptr);

static struct sh_css_hmm_buffer_record
*sh_css_hmm_buffer_record_validate(ia_css_ptr ddr_buffer_addr,
				   enum ia_css_buffer_type type);

void
ia_css_get_acc_configs(
    struct ia_css_pipe *pipe,
    struct ia_css_isp_config *config);

#if CONFIG_ON_FRAME_ENQUEUE()
static int set_config_on_frame_enqueue(struct ia_css_frame_info
	*info, struct frame_data_wrapper *frame);
#endif

#ifdef USE_INPUT_SYSTEM_VERSION_2401
static unsigned int get_crop_lines_for_bayer_order(const struct
	ia_css_stream_config *config);
static unsigned int get_crop_columns_for_bayer_order(const struct
	ia_css_stream_config *config);
static void get_pipe_extra_pixel(struct ia_css_pipe *pipe,
				 unsigned int *extra_row, unsigned int *extra_column);
static int
aspect_ratio_crop_init(struct ia_css_stream *curr_stream,
		       struct ia_css_pipe *pipes[],
		       bool *do_crop_status);

static bool
aspect_ratio_crop_check(bool enabled, struct ia_css_pipe *curr_pipe);

static int
aspect_ratio_crop(struct ia_css_pipe *curr_pipe,
		  struct ia_css_resolution *effective_res);
#endif

static void
sh_css_pipe_free_shading_table(struct ia_css_pipe *pipe)
{
	assert(pipe);
	if (!pipe) {
		IA_CSS_ERROR("NULL input parameter");
		return;
	}

	if (pipe->shading_table)
		ia_css_shading_table_free(pipe->shading_table);
	pipe->shading_table = NULL;
}

static enum ia_css_frame_format yuv420_copy_formats[] = {
	IA_CSS_FRAME_FORMAT_NV12,
	IA_CSS_FRAME_FORMAT_NV21,
	IA_CSS_FRAME_FORMAT_YV12,
	IA_CSS_FRAME_FORMAT_YUV420,
	IA_CSS_FRAME_FORMAT_YUV420_16,
	IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8,
	IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8
};

static enum ia_css_frame_format yuv422_copy_formats[] = {
	IA_CSS_FRAME_FORMAT_NV12,
	IA_CSS_FRAME_FORMAT_NV16,
	IA_CSS_FRAME_FORMAT_NV21,
	IA_CSS_FRAME_FORMAT_NV61,
	IA_CSS_FRAME_FORMAT_YV12,
	IA_CSS_FRAME_FORMAT_YV16,
	IA_CSS_FRAME_FORMAT_YUV420,
	IA_CSS_FRAME_FORMAT_YUV420_16,
	IA_CSS_FRAME_FORMAT_YUV422,
	IA_CSS_FRAME_FORMAT_YUV422_16,
	IA_CSS_FRAME_FORMAT_UYVY,
	IA_CSS_FRAME_FORMAT_YUYV
};

/* Verify whether the selected output format is can be produced
 * by the copy binary given the stream format.
 * */
static int
verify_copy_out_frame_format(struct ia_css_pipe *pipe) {
	enum ia_css_frame_format out_fmt = pipe->output_info[0].format;
	unsigned int i, found = 0;

	assert(pipe);
	assert(pipe->stream);

	switch (pipe->stream->config.input_config.format)
	{
	case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
	case ATOMISP_INPUT_FORMAT_YUV420_8:
		for (i = 0; i < ARRAY_SIZE(yuv420_copy_formats) && !found; i++)
			found = (out_fmt == yuv420_copy_formats[i]);
		break;
	case ATOMISP_INPUT_FORMAT_YUV420_10:
	case ATOMISP_INPUT_FORMAT_YUV420_16:
		found = (out_fmt == IA_CSS_FRAME_FORMAT_YUV420_16);
		break;
	case ATOMISP_INPUT_FORMAT_YUV422_8:
		for (i = 0; i < ARRAY_SIZE(yuv422_copy_formats) && !found; i++)
			found = (out_fmt == yuv422_copy_formats[i]);
		break;
	case ATOMISP_INPUT_FORMAT_YUV422_10:
	case ATOMISP_INPUT_FORMAT_YUV422_16:
		found = (out_fmt == IA_CSS_FRAME_FORMAT_YUV422_16 ||
			 out_fmt == IA_CSS_FRAME_FORMAT_YUV420_16);
		break;
	case ATOMISP_INPUT_FORMAT_RGB_444:
	case ATOMISP_INPUT_FORMAT_RGB_555:
	case ATOMISP_INPUT_FORMAT_RGB_565:
		found = (out_fmt == IA_CSS_FRAME_FORMAT_RGBA888 ||
			 out_fmt == IA_CSS_FRAME_FORMAT_RGB565);
		break;
	case ATOMISP_INPUT_FORMAT_RGB_666:
	case ATOMISP_INPUT_FORMAT_RGB_888:
		found = (out_fmt == IA_CSS_FRAME_FORMAT_RGBA888 ||
			 out_fmt == IA_CSS_FRAME_FORMAT_YUV420);
		break;
	case ATOMISP_INPUT_FORMAT_RAW_6:
	case ATOMISP_INPUT_FORMAT_RAW_7:
	case ATOMISP_INPUT_FORMAT_RAW_8:
	case ATOMISP_INPUT_FORMAT_RAW_10:
	case ATOMISP_INPUT_FORMAT_RAW_12:
	case ATOMISP_INPUT_FORMAT_RAW_14:
	case ATOMISP_INPUT_FORMAT_RAW_16:
		found = (out_fmt == IA_CSS_FRAME_FORMAT_RAW) ||
		(out_fmt == IA_CSS_FRAME_FORMAT_RAW_PACKED);
		break;
	case ATOMISP_INPUT_FORMAT_BINARY_8:
		found = (out_fmt == IA_CSS_FRAME_FORMAT_BINARY_8);
		break;
	default:
		break;
	}
	if (!found)
		return -EINVAL;
	return 0;
}

unsigned int
ia_css_stream_input_format_bits_per_pixel(struct ia_css_stream *stream)
{
	int bpp = 0;

	if (stream)
		bpp = ia_css_util_input_format_bpp(stream->config.input_config.format,
						   stream->config.pixels_per_clock == 2);

	return bpp;
}

#define GP_ISEL_TPG_MODE 0x90058

#if !defined(HAS_NO_INPUT_SYSTEM) && defined(USE_INPUT_SYSTEM_VERSION_2)
static int
sh_css_config_input_network(struct ia_css_stream *stream) {
	unsigned int fmt_type;
	struct ia_css_pipe *pipe = stream->last_pipe;
	struct ia_css_binary *binary = NULL;
	int err = 0;

	assert(stream);
	assert(pipe);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_config_input_network() enter:\n");

	if (pipe->pipeline.stages)
		binary = pipe->pipeline.stages->binary;

	err = ia_css_isys_convert_stream_format_to_mipi_format(
	    stream->config.input_config.format,
	    stream->csi_rx_config.comp,
	    &fmt_type);
	if (err)
		return err;
	sh_css_sp_program_input_circuit(fmt_type,
					stream->config.channel_id,
					stream->config.mode);

	if ((binary && (binary->online || stream->config.continuous)) ||
	    pipe->config.mode == IA_CSS_PIPE_MODE_COPY)
	{
		err = ia_css_ifmtr_configure(&stream->config,
					     binary);
		if (err)
			return err;
	}

	if (stream->config.mode == IA_CSS_INPUT_MODE_TPG ||
	    stream->config.mode == IA_CSS_INPUT_MODE_PRBS)
	{
		unsigned int hblank_cycles = 100,
		vblank_lines = 6,
		width,
		height,
		vblank_cycles;
		width  = (stream->config.input_config.input_res.width) / (1 +
			(stream->config.pixels_per_clock == 2));
		height = stream->config.input_config.input_res.height;
		vblank_cycles = vblank_lines * (width + hblank_cycles);
		sh_css_sp_configure_sync_gen(width, height, hblank_cycles,
					     vblank_cycles);
		if (!IS_ISP2401) {
			if (pipe->stream->config.mode == IA_CSS_INPUT_MODE_TPG) {
				/* TODO: move define to proper file in tools */
				ia_css_device_store_uint32(GP_ISEL_TPG_MODE, 0);
			}
		}
	}
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_config_input_network() leave:\n");
	return 0;
}
#elif !defined(HAS_NO_INPUT_SYSTEM) && defined(USE_INPUT_SYSTEM_VERSION_2401)
static unsigned int csi2_protocol_calculate_max_subpixels_per_line(
    enum atomisp_input_format	format,
    unsigned int			pixels_per_line)
{
	unsigned int rval;

	switch (format) {
	case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
		/*
		 * The frame format layout is shown below.
		 *
		 *		Line	0:	UYY0 UYY0 ... UYY0
		 *		Line	1:	VYY0 VYY0 ... VYY0
		 *		Line	2:	UYY0 UYY0 ... UYY0
		 *		Line	3:	VYY0 VYY0 ... VYY0
		 *		...
		 *		Line (n-2):	UYY0 UYY0 ... UYY0
		 *		Line (n-1):	VYY0 VYY0 ... VYY0
		 *
		 *	In this frame format, the even-line is
		 *	as wide as the odd-line.
		 *	The 0 is introduced by the input system
		 *	(mipi backend).
		 */
		rval = pixels_per_line * 2;
		break;
	case ATOMISP_INPUT_FORMAT_YUV420_8:
	case ATOMISP_INPUT_FORMAT_YUV420_10:
	case ATOMISP_INPUT_FORMAT_YUV420_16:
		/*
		 * The frame format layout is shown below.
		 *
		 *		Line	0:	YYYY YYYY ... YYYY
		 *		Line	1:	UYVY UYVY ... UYVY UYVY
		 *		Line	2:	YYYY YYYY ... YYYY
		 *		Line	3:	UYVY UYVY ... UYVY UYVY
		 *		...
		 *		Line (n-2):	YYYY YYYY ... YYYY
		 *		Line (n-1):	UYVY UYVY ... UYVY UYVY
		 *
		 * In this frame format, the odd-line is twice
		 * wider than the even-line.
		 */
		rval = pixels_per_line * 2;
		break;
	case ATOMISP_INPUT_FORMAT_YUV422_8:
	case ATOMISP_INPUT_FORMAT_YUV422_10:
	case ATOMISP_INPUT_FORMAT_YUV422_16:
		/*
		 * The frame format layout is shown below.
		 *
		 *		Line	0:	UYVY UYVY ... UYVY
		 *		Line	1:	UYVY UYVY ... UYVY
		 *		Line	2:	UYVY UYVY ... UYVY
		 *		Line	3:	UYVY UYVY ... UYVY
		 *		...
		 *		Line (n-2):	UYVY UYVY ... UYVY
		 *		Line (n-1):	UYVY UYVY ... UYVY
		 *
		 * In this frame format, the even-line is
		 * as wide as the odd-line.
		 */
		rval = pixels_per_line * 2;
		break;
	case ATOMISP_INPUT_FORMAT_RGB_444:
	case ATOMISP_INPUT_FORMAT_RGB_555:
	case ATOMISP_INPUT_FORMAT_RGB_565:
	case ATOMISP_INPUT_FORMAT_RGB_666:
	case ATOMISP_INPUT_FORMAT_RGB_888:
		/*
		 * The frame format layout is shown below.
		 *
		 *		Line	0:	ABGR ABGR ... ABGR
		 *		Line	1:	ABGR ABGR ... ABGR
		 *		Line	2:	ABGR ABGR ... ABGR
		 *		Line	3:	ABGR ABGR ... ABGR
		 *		...
		 *		Line (n-2):	ABGR ABGR ... ABGR
		 *		Line (n-1):	ABGR ABGR ... ABGR
		 *
		 * In this frame format, the even-line is
		 * as wide as the odd-line.
		 */
		rval = pixels_per_line * 4;
		break;
	case ATOMISP_INPUT_FORMAT_RAW_6:
	case ATOMISP_INPUT_FORMAT_RAW_7:
	case ATOMISP_INPUT_FORMAT_RAW_8:
	case ATOMISP_INPUT_FORMAT_RAW_10:
	case ATOMISP_INPUT_FORMAT_RAW_12:
	case ATOMISP_INPUT_FORMAT_RAW_14:
	case ATOMISP_INPUT_FORMAT_RAW_16:
	case ATOMISP_INPUT_FORMAT_BINARY_8:
	case ATOMISP_INPUT_FORMAT_USER_DEF1:
	case ATOMISP_INPUT_FORMAT_USER_DEF2:
	case ATOMISP_INPUT_FORMAT_USER_DEF3:
	case ATOMISP_INPUT_FORMAT_USER_DEF4:
	case ATOMISP_INPUT_FORMAT_USER_DEF5:
	case ATOMISP_INPUT_FORMAT_USER_DEF6:
	case ATOMISP_INPUT_FORMAT_USER_DEF7:
	case ATOMISP_INPUT_FORMAT_USER_DEF8:
		/*
		 * The frame format layout is shown below.
		 *
		 *		Line	0:	Pixel Pixel ... Pixel
		 *		Line	1:	Pixel Pixel ... Pixel
		 *		Line	2:	Pixel Pixel ... Pixel
		 *		Line	3:	Pixel Pixel ... Pixel
		 *		...
		 *		Line (n-2):	Pixel Pixel ... Pixel
		 *		Line (n-1):	Pixel Pixel ... Pixel
		 *
		 * In this frame format, the even-line is
		 * as wide as the odd-line.
		 */
		rval = pixels_per_line;
		break;
	default:
		rval = 0;
		break;
	}

	return rval;
}

static bool sh_css_translate_stream_cfg_to_input_system_input_port_id(
    struct ia_css_stream_config *stream_cfg,
    ia_css_isys_descr_t	*isys_stream_descr)
{
	bool rc;

	rc = true;
	switch (stream_cfg->mode) {
	case IA_CSS_INPUT_MODE_TPG:

		if (stream_cfg->source.tpg.id == IA_CSS_TPG_ID0) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT0_ID;
		} else if (stream_cfg->source.tpg.id == IA_CSS_TPG_ID1) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT1_ID;
		} else if (stream_cfg->source.tpg.id == IA_CSS_TPG_ID2) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT2_ID;
		}

		break;
	case IA_CSS_INPUT_MODE_PRBS:

		if (stream_cfg->source.prbs.id == IA_CSS_PRBS_ID0) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT0_ID;
		} else if (stream_cfg->source.prbs.id == IA_CSS_PRBS_ID1) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT1_ID;
		} else if (stream_cfg->source.prbs.id == IA_CSS_PRBS_ID2) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_PIXELGEN_PORT2_ID;
		}

		break;
	case IA_CSS_INPUT_MODE_BUFFERED_SENSOR:

		if (stream_cfg->source.port.port == MIPI_PORT0_ID) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_CSI_PORT0_ID;
		} else if (stream_cfg->source.port.port == MIPI_PORT1_ID) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_CSI_PORT1_ID;
		} else if (stream_cfg->source.port.port == MIPI_PORT2_ID) {
			isys_stream_descr->input_port_id = INPUT_SYSTEM_CSI_PORT2_ID;
		}

		break;
	default:
		rc = false;
		break;
	}

	return rc;
}

static bool sh_css_translate_stream_cfg_to_input_system_input_port_type(
    struct ia_css_stream_config *stream_cfg,
    ia_css_isys_descr_t	*isys_stream_descr)
{
	bool rc;

	rc = true;
	switch (stream_cfg->mode) {
	case IA_CSS_INPUT_MODE_TPG:

		isys_stream_descr->mode = INPUT_SYSTEM_SOURCE_TYPE_TPG;

		break;
	case IA_CSS_INPUT_MODE_PRBS:

		isys_stream_descr->mode = INPUT_SYSTEM_SOURCE_TYPE_PRBS;

		break;
	case IA_CSS_INPUT_MODE_SENSOR:
	case IA_CSS_INPUT_MODE_BUFFERED_SENSOR:

		isys_stream_descr->mode = INPUT_SYSTEM_SOURCE_TYPE_SENSOR;
		break;

	default:
		rc = false;
		break;
	}

	return rc;
}

static bool sh_css_translate_stream_cfg_to_input_system_input_port_attr(
    struct ia_css_stream_config *stream_cfg,
    ia_css_isys_descr_t	*isys_stream_descr,
    int isys_stream_idx)
{
	bool rc;

	rc = true;
	switch (stream_cfg->mode) {
	case IA_CSS_INPUT_MODE_TPG:
		if (stream_cfg->source.tpg.mode == IA_CSS_TPG_MODE_RAMP) {
			isys_stream_descr->tpg_port_attr.mode = PIXELGEN_TPG_MODE_RAMP;
		} else if (stream_cfg->source.tpg.mode == IA_CSS_TPG_MODE_CHECKERBOARD) {
			isys_stream_descr->tpg_port_attr.mode = PIXELGEN_TPG_MODE_CHBO;
		} else if (stream_cfg->source.tpg.mode == IA_CSS_TPG_MODE_MONO) {
			isys_stream_descr->tpg_port_attr.mode = PIXELGEN_TPG_MODE_MONO;
		} else {
			rc = false;
		}

		/*
		 * TODO
		 * - Make "color_cfg" as part of "ia_css_tpg_config".
		 */
		isys_stream_descr->tpg_port_attr.color_cfg.R1 = 51;
		isys_stream_descr->tpg_port_attr.color_cfg.G1 = 102;
		isys_stream_descr->tpg_port_attr.color_cfg.B1 = 255;
		isys_stream_descr->tpg_port_attr.color_cfg.R2 = 0;
		isys_stream_descr->tpg_port_attr.color_cfg.G2 = 100;
		isys_stream_descr->tpg_port_attr.color_cfg.B2 = 160;

		isys_stream_descr->tpg_port_attr.mask_cfg.h_mask =
		    stream_cfg->source.tpg.x_mask;
		isys_stream_descr->tpg_port_attr.mask_cfg.v_mask =
		    stream_cfg->source.tpg.y_mask;
		isys_stream_descr->tpg_port_attr.mask_cfg.hv_mask =
		    stream_cfg->source.tpg.xy_mask;

		isys_stream_descr->tpg_port_attr.delta_cfg.h_delta =
		    stream_cfg->source.tpg.x_delta;
		isys_stream_descr->tpg_port_attr.delta_cfg.v_delta =
		    stream_cfg->source.tpg.y_delta;

		/*
		 * TODO
		 * - Make "sync_gen_cfg" as part of "ia_css_tpg_config".
		 */
		isys_stream_descr->tpg_port_attr.sync_gen_cfg.hblank_cycles = 100;
		isys_stream_descr->tpg_port_attr.sync_gen_cfg.vblank_cycles = 100;
		isys_stream_descr->tpg_port_attr.sync_gen_cfg.pixels_per_clock =
		    stream_cfg->pixels_per_clock;
		isys_stream_descr->tpg_port_attr.sync_gen_cfg.nr_of_frames = (uint32_t)~(0x0);
		isys_stream_descr->tpg_port_attr.sync_gen_cfg.pixels_per_line =
		    stream_cfg->isys_config[IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX].input_res.width;
		isys_stream_descr->tpg_port_attr.sync_gen_cfg.lines_per_frame =
		    stream_cfg->isys_config[IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX].input_res.height;

		break;
	case IA_CSS_INPUT_MODE_PRBS:

		isys_stream_descr->prbs_port_attr.seed0 = stream_cfg->source.prbs.seed;
		isys_stream_descr->prbs_port_attr.seed1 = stream_cfg->source.prbs.seed1;

		/*
		 * TODO
		 * - Make "sync_gen_cfg" as part of "ia_css_prbs_config".
		 */
		isys_stream_descr->prbs_port_attr.sync_gen_cfg.hblank_cycles = 100;
		isys_stream_descr->prbs_port_attr.sync_gen_cfg.vblank_cycles = 100;
		isys_stream_descr->prbs_port_attr.sync_gen_cfg.pixels_per_clock =
		    stream_cfg->pixels_per_clock;
		isys_stream_descr->prbs_port_attr.sync_gen_cfg.nr_of_frames = (uint32_t)~(0x0);
		isys_stream_descr->prbs_port_attr.sync_gen_cfg.pixels_per_line =
		    stream_cfg->isys_config[IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX].input_res.width;
		isys_stream_descr->prbs_port_attr.sync_gen_cfg.lines_per_frame =
		    stream_cfg->isys_config[IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX].input_res.height;

		break;
	case IA_CSS_INPUT_MODE_BUFFERED_SENSOR: {
		int err;
		unsigned int fmt_type;

		err = ia_css_isys_convert_stream_format_to_mipi_format(
			  stream_cfg->isys_config[isys_stream_idx].format,
			  MIPI_PREDICTOR_NONE,
			  &fmt_type);
		if (err)
			rc = false;

		isys_stream_descr->csi_port_attr.active_lanes =
		    stream_cfg->source.port.num_lanes;
		isys_stream_descr->csi_port_attr.fmt_type = fmt_type;
		isys_stream_descr->csi_port_attr.ch_id = stream_cfg->channel_id;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
		isys_stream_descr->online = stream_cfg->online;
#endif
		err |= ia_css_isys_convert_compressed_format(
			   &stream_cfg->source.port.compression,
			   isys_stream_descr);
		if (err)
			rc = false;

		/* metadata */
		isys_stream_descr->metadata.enable = false;
		if (stream_cfg->metadata_config.resolution.height > 0) {
			err = ia_css_isys_convert_stream_format_to_mipi_format(
				  stream_cfg->metadata_config.data_type,
				  MIPI_PREDICTOR_NONE,
				  &fmt_type);
			if (err)
				rc = false;
			isys_stream_descr->metadata.fmt_type = fmt_type;
			isys_stream_descr->metadata.bits_per_pixel =
			    ia_css_util_input_format_bpp(stream_cfg->metadata_config.data_type, true);
			isys_stream_descr->metadata.pixels_per_line =
			    stream_cfg->metadata_config.resolution.width;
			isys_stream_descr->metadata.lines_per_frame =
			    stream_cfg->metadata_config.resolution.height;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
			/* For new input system, number of str2mmio requests must be even.
			 * So we round up number of metadata lines to be even. */
			if (isys_stream_descr->metadata.lines_per_frame > 0)
				isys_stream_descr->metadata.lines_per_frame +=
				    (isys_stream_descr->metadata.lines_per_frame & 1);
#endif
			isys_stream_descr->metadata.align_req_in_bytes =
			    ia_css_csi2_calculate_input_system_alignment(
				stream_cfg->metadata_config.data_type);
			isys_stream_descr->metadata.enable = true;
		}

		break;
	}
	default:
		rc = false;
		break;
	}

	return rc;
}

static bool sh_css_translate_stream_cfg_to_input_system_input_port_resolution(
    struct ia_css_stream_config *stream_cfg,
    ia_css_isys_descr_t	*isys_stream_descr,
    int isys_stream_idx)
{
	unsigned int bits_per_subpixel;
	unsigned int max_subpixels_per_line;
	unsigned int lines_per_frame;
	unsigned int align_req_in_bytes;
	enum atomisp_input_format fmt_type;

	fmt_type = stream_cfg->isys_config[isys_stream_idx].format;
	if ((stream_cfg->mode == IA_CSS_INPUT_MODE_SENSOR ||
	     stream_cfg->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) &&
	    stream_cfg->source.port.compression.type != IA_CSS_CSI2_COMPRESSION_TYPE_NONE) {
		if (stream_cfg->source.port.compression.uncompressed_bits_per_pixel ==
		    UNCOMPRESSED_BITS_PER_PIXEL_10) {
			fmt_type = ATOMISP_INPUT_FORMAT_RAW_10;
		} else if (stream_cfg->source.port.compression.uncompressed_bits_per_pixel ==
			   UNCOMPRESSED_BITS_PER_PIXEL_12) {
			fmt_type = ATOMISP_INPUT_FORMAT_RAW_12;
		} else
			return false;
	}

	bits_per_subpixel =
	    sh_css_stream_format_2_bits_per_subpixel(fmt_type);
	if (bits_per_subpixel == 0)
		return false;

	max_subpixels_per_line =
	    csi2_protocol_calculate_max_subpixels_per_line(fmt_type,
		    stream_cfg->isys_config[isys_stream_idx].input_res.width);
	if (max_subpixels_per_line == 0)
		return false;

	lines_per_frame = stream_cfg->isys_config[isys_stream_idx].input_res.height;
	if (lines_per_frame == 0)
		return false;

	align_req_in_bytes = ia_css_csi2_calculate_input_system_alignment(fmt_type);

	/* HW needs subpixel info for their settings */
	isys_stream_descr->input_port_resolution.bits_per_pixel = bits_per_subpixel;
	isys_stream_descr->input_port_resolution.pixels_per_line =
	    max_subpixels_per_line;
	isys_stream_descr->input_port_resolution.lines_per_frame = lines_per_frame;
	isys_stream_descr->input_port_resolution.align_req_in_bytes =
	    align_req_in_bytes;

	return true;
}

static bool sh_css_translate_stream_cfg_to_isys_stream_descr(
    struct ia_css_stream_config *stream_cfg,
    bool early_polling,
    ia_css_isys_descr_t	*isys_stream_descr,
    int isys_stream_idx)
{
	bool rc;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_translate_stream_cfg_to_isys_stream_descr() enter:\n");
	rc  = sh_css_translate_stream_cfg_to_input_system_input_port_id(stream_cfg,
		isys_stream_descr);
	rc &= sh_css_translate_stream_cfg_to_input_system_input_port_type(stream_cfg,
		isys_stream_descr);
	rc &= sh_css_translate_stream_cfg_to_input_system_input_port_attr(stream_cfg,
		isys_stream_descr, isys_stream_idx);
	rc &= sh_css_translate_stream_cfg_to_input_system_input_port_resolution(
		  stream_cfg, isys_stream_descr, isys_stream_idx);

	isys_stream_descr->raw_packed = stream_cfg->pack_raw_pixels;
	isys_stream_descr->linked_isys_stream_id = (int8_t)
		stream_cfg->isys_config[isys_stream_idx].linked_isys_stream_id;
	/*
	 * Early polling is required for timestamp accuracy in certain case.
	 * The ISYS HW polling is started on
	 * ia_css_isys_stream_capture_indication() instead of
	 * ia_css_pipeline_sp_wait_for_isys_stream_N() as isp processing of
	 * capture takes longer than getting an ISYS frame
	 *
	 * Only 2401 relevant ??
	 */
#if 0 // FIXME: NOT USED on Yocto Aero
	isys_stream_descr->polling_mode
	    = early_polling ? INPUT_SYSTEM_POLL_ON_CAPTURE_REQUEST
	      : INPUT_SYSTEM_POLL_ON_WAIT_FOR_FRAME;
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_translate_stream_cfg_to_isys_stream_descr() leave:\n");
#endif

	return rc;
}

static bool sh_css_translate_binary_info_to_input_system_output_port_attr(
    struct ia_css_binary *binary,
    ia_css_isys_descr_t     *isys_stream_descr)
{
	if (!binary)
		return false;

	isys_stream_descr->output_port_attr.left_padding = binary->left_padding;
	isys_stream_descr->output_port_attr.max_isp_input_width =
	    binary->info->sp.input.max_width;

	return true;
}

static int
sh_css_config_input_network(struct ia_css_stream *stream) {
	bool					rc;
	ia_css_isys_descr_t			isys_stream_descr;
	unsigned int				sp_thread_id;
	struct sh_css_sp_pipeline_terminal	*sp_pipeline_input_terminal;
	struct ia_css_pipe *pipe = NULL;
	struct ia_css_binary *binary = NULL;
	int i;
	u32 isys_stream_id;
	bool early_polling = false;

	assert(stream);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_config_input_network() enter 0x%p:\n", stream);

	if (stream->config.continuous == true)
	{
		if (stream->last_pipe->config.mode == IA_CSS_PIPE_MODE_CAPTURE) {
			pipe = stream->last_pipe;
		} else if (stream->last_pipe->config.mode == IA_CSS_PIPE_MODE_YUVPP) {
			pipe = stream->last_pipe;
		} else if (stream->last_pipe->config.mode == IA_CSS_PIPE_MODE_PREVIEW) {
			pipe = stream->last_pipe->pipe_settings.preview.copy_pipe;
		} else if (stream->last_pipe->config.mode == IA_CSS_PIPE_MODE_VIDEO) {
			pipe = stream->last_pipe->pipe_settings.video.copy_pipe;
		}
	} else
	{
		pipe = stream->last_pipe;
		if (stream->last_pipe->config.mode == IA_CSS_PIPE_MODE_CAPTURE) {
			/*
			 * We need to poll the ISYS HW in capture_indication itself
			 * for "non-continuous" capture usecase for getting accurate
			 * isys frame capture timestamps.
			 * This is because the capturepipe propcessing takes longer
			 * to execute than the input system frame capture.
			 * 2401 specific
			 */
			early_polling = true;
		}
	}

	assert(pipe);
	if (!pipe)
		return -EINVAL;

	if (pipe->pipeline.stages)
		if (pipe->pipeline.stages->binary)
			binary = pipe->pipeline.stages->binary;

	if (binary)
	{
		/* this was being done in ifmtr in 2400.
		 * online and cont bypass the init_in_frameinfo_memory_defaults
		 * so need to do it here
		 */
		ia_css_get_crop_offsets(pipe, &binary->in_frame_info);
	}

	/* get the SP thread id */
	rc = ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &sp_thread_id);
	if (!rc)
		return -EINVAL;
	/* get the target input terminal */
	sp_pipeline_input_terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input;

	for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++)
	{
		/* initialization */
		memset((void *)(&isys_stream_descr), 0, sizeof(ia_css_isys_descr_t));
		sp_pipeline_input_terminal->context.virtual_input_system_stream[i].valid = 0;
		sp_pipeline_input_terminal->ctrl.virtual_input_system_stream_cfg[i].valid = 0;

		if (!stream->config.isys_config[i].valid)
			continue;

		/* translate the stream configuration to the Input System (2401) configuration */
		rc = sh_css_translate_stream_cfg_to_isys_stream_descr(
			 &stream->config,
			 early_polling,
			 &(isys_stream_descr), i);

		if (stream->config.online) {
			rc &= sh_css_translate_binary_info_to_input_system_output_port_attr(
				  binary,
				  &(isys_stream_descr));
		}

		if (!rc)
			return -EINVAL;

		isys_stream_id = ia_css_isys_generate_stream_id(sp_thread_id, i);

		/* create the virtual Input System (2401) */
		rc =  ia_css_isys_stream_create(
			  &(isys_stream_descr),
			  &sp_pipeline_input_terminal->context.virtual_input_system_stream[i],
			  isys_stream_id);
		if (!rc)
			return -EINVAL;

		/* calculate the configuration of the virtual Input System (2401) */
		rc = ia_css_isys_stream_calculate_cfg(
			 &sp_pipeline_input_terminal->context.virtual_input_system_stream[i],
			 &(isys_stream_descr),
			 &sp_pipeline_input_terminal->ctrl.virtual_input_system_stream_cfg[i]);
		if (!rc) {
			ia_css_isys_stream_destroy(
			    &sp_pipeline_input_terminal->context.virtual_input_system_stream[i]);
			return -EINVAL;
		}
	}

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_config_input_network() leave:\n");

	return 0;
}

static inline struct ia_css_pipe *stream_get_last_pipe(
    struct ia_css_stream *stream)
{
	struct ia_css_pipe *last_pipe = NULL;

	if (stream)
		last_pipe = stream->last_pipe;

	return last_pipe;
}

static inline struct ia_css_pipe *stream_get_copy_pipe(
    struct ia_css_stream *stream)
{
	struct ia_css_pipe *copy_pipe = NULL;
	struct ia_css_pipe *last_pipe = NULL;
	enum ia_css_pipe_id pipe_id;

	last_pipe = stream_get_last_pipe(stream);

	if ((stream) &&
	    (last_pipe) &&
	    (stream->config.continuous)) {
		pipe_id = last_pipe->mode;
		switch (pipe_id) {
		case IA_CSS_PIPE_ID_PREVIEW:
			copy_pipe = last_pipe->pipe_settings.preview.copy_pipe;
			break;
		case IA_CSS_PIPE_ID_VIDEO:
			copy_pipe = last_pipe->pipe_settings.video.copy_pipe;
			break;
		default:
			copy_pipe = NULL;
			break;
		}
	}

	return copy_pipe;
}

static inline struct ia_css_pipe *stream_get_target_pipe(
    struct ia_css_stream *stream)
{
	struct ia_css_pipe *target_pipe;

	/* get the pipe that consumes the stream */
	if (stream->config.continuous) {
		target_pipe = stream_get_copy_pipe(stream);
	} else {
		target_pipe = stream_get_last_pipe(stream);
	}

	return target_pipe;
}

static int stream_csi_rx_helper(
    struct ia_css_stream *stream,
    int (*func)(enum mipi_port_id, uint32_t))
{
	int retval = -EINVAL;
	u32 sp_thread_id, stream_id;
	bool rc;
	struct ia_css_pipe *target_pipe = NULL;

	if ((!stream) || (stream->config.mode != IA_CSS_INPUT_MODE_BUFFERED_SENSOR))
		goto exit;

	target_pipe = stream_get_target_pipe(stream);

	if (!target_pipe)
		goto exit;

	rc = ia_css_pipeline_get_sp_thread_id(
		 ia_css_pipe_get_pipe_num(target_pipe),
		 &sp_thread_id);

	if (!rc)
		goto exit;

	/* (un)register all valid "virtual isys streams" within the ia_css_stream */
	stream_id = 0;
	do {
		if (stream->config.isys_config[stream_id].valid) {
			u32 isys_stream_id = ia_css_isys_generate_stream_id(sp_thread_id, stream_id);

			retval = func(stream->config.source.port.port, isys_stream_id);
		}
		stream_id++;
	} while ((retval == 0) &&
		 (stream_id < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH));

exit:
	return retval;
}

static inline int stream_register_with_csi_rx(
    struct ia_css_stream *stream)
{
	return stream_csi_rx_helper(stream, ia_css_isys_csi_rx_register_stream);
}

static inline int stream_unregister_with_csi_rx(
    struct ia_css_stream *stream)
{
	return stream_csi_rx_helper(stream, ia_css_isys_csi_rx_unregister_stream);
}
#endif

#if WITH_PC_MONITORING
static struct task_struct *my_kthread;    /* Handle for the monitoring thread */
static int sh_binary_running;         /* Enable sampling in the thread */

static void print_pc_histo(char *core_name, struct sh_css_pc_histogram *hist)
{
	unsigned int i;
	unsigned int cnt_run = 0;
	unsigned int cnt_stall = 0;

	if (!hist)
		return;

	sh_css_print("%s histogram length = %d\n", core_name, hist->length);
	sh_css_print("%s PC\turn\tstall\n", core_name);

	for (i = 0; i < hist->length; i++) {
		if ((hist->run[i] == 0) && (hist->run[i] == hist->stall[i]))
			continue;
		sh_css_print("%s %d\t%d\t%d\n",
			     core_name, i, hist->run[i], hist->stall[i]);
		cnt_run += hist->run[i];
		cnt_stall += hist->stall[i];
	}

	sh_css_print(" Statistics for %s, cnt_run = %d, cnt_stall = %d, hist->length = %d\n",
		     core_name, cnt_run, cnt_stall, hist->length);
}

static void print_pc_histogram(void)
{
	struct ia_css_binary_metrics *metrics;

	for (metrics = sh_css_metrics.binary_metrics;
	     metrics;
	     metrics = metrics->next) {
		if (metrics->mode == IA_CSS_BINARY_MODE_PREVIEW ||
		    metrics->mode == IA_CSS_BINARY_MODE_VF_PP) {
			sh_css_print("pc_histogram for binary %d is SKIPPED\n",
				     metrics->id);
			continue;
		}

		sh_css_print(" pc_histogram for binary %d\n", metrics->id);
		print_pc_histo("  ISP", &metrics->isp_histogram);
		print_pc_histo("  SP",   &metrics->sp_histogram);
		sh_css_print("print_pc_histogram() done for binary->id = %d, done.\n",
			     metrics->id);
	}

	sh_css_print("PC_MONITORING:print_pc_histogram() -- DONE\n");
}

static int pc_monitoring(void *data)
{
	int i = 0;

	(void)data;
	while (true) {
		if (sh_binary_running) {
			sh_css_metrics_sample_pcs();
#if MULTIPLE_SAMPLES
			for (i = 0; i < NOF_SAMPLES; i++)
				sh_css_metrics_sample_pcs();
#endif
		}
		usleep_range(10, 50);
	}
	return 0;
}

static void spying_thread_create(void)
{
	my_kthread = kthread_run(pc_monitoring, NULL, "sh_pc_monitor");
	sh_css_metrics_enable_pc_histogram(1);
}

static void input_frame_info(struct ia_css_frame_info frame_info)
{
	sh_css_print("SH_CSS:input_frame_info() -- frame->info.res.width = %d, frame->info.res.height = %d, format = %d\n",
		     frame_info.res.width, frame_info.res.height, frame_info.format);
}
#endif /* WITH_PC_MONITORING */

static void
start_binary(struct ia_css_pipe *pipe,
	     struct ia_css_binary *binary)
{
	struct ia_css_stream *stream;

	assert(pipe);
	/* Acceleration uses firmware, the binary thus can be NULL */
	/* assert(binary != NULL); */

	(void)binary;

#if !defined(HAS_NO_INPUT_SYSTEM)
	stream = pipe->stream;
#else
	(void)pipe;
	(void)stream;
#endif

	if (binary)
		sh_css_metrics_start_binary(&binary->metrics);

#if WITH_PC_MONITORING
	sh_css_print("PC_MONITORING: %s() -- binary id = %d , enable_dvs_envelope = %d\n",
		     __func__, binary->info->sp.id,
		     binary->info->sp.enable.dvs_envelope);
	input_frame_info(binary->in_frame_info);

	if (binary && binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_VIDEO)
		sh_binary_running = true;
#endif

#if !defined(HAS_NO_INPUT_SYSTEM) && !defined(USE_INPUT_SYSTEM_VERSION_2401)
	if (stream->reconfigure_css_rx) {
		ia_css_isys_rx_configure(&pipe->stream->csi_rx_config,
					 pipe->stream->config.mode);
		stream->reconfigure_css_rx = false;
	}
#endif
}

/* start the copy function on the SP */
static int
start_copy_on_sp(struct ia_css_pipe *pipe,
		 struct ia_css_frame *out_frame) {
	(void)out_frame;
	assert(pipe);
	assert(pipe->stream);

	if ((!pipe) || (!pipe->stream))
		return -EINVAL;

#if !defined(HAS_NO_INPUT_SYSTEM) && !defined(USE_INPUT_SYSTEM_VERSION_2401)
	if (pipe->stream->reconfigure_css_rx)
		ia_css_isys_rx_disable();
#endif

	if (pipe->stream->config.input_config.format != ATOMISP_INPUT_FORMAT_BINARY_8)
		return -EINVAL;
	sh_css_sp_start_binary_copy(ia_css_pipe_get_pipe_num(pipe), out_frame, pipe->stream->config.pixels_per_clock == 2);

#if !defined(HAS_NO_INPUT_SYSTEM) && !defined(USE_INPUT_SYSTEM_VERSION_2401)
	if (pipe->stream->reconfigure_css_rx)
	{
		ia_css_isys_rx_configure(&pipe->stream->csi_rx_config,
					 pipe->stream->config.mode);
		pipe->stream->reconfigure_css_rx = false;
	}
#endif

	return 0;
}

void sh_css_binary_args_reset(struct sh_css_binary_args *args)
{
	unsigned int i;

	for (i = 0; i < NUM_TNR_FRAMES; i++)
		args->tnr_frames[i] = NULL;
	for (i = 0; i < MAX_NUM_VIDEO_DELAY_FRAMES; i++)
		args->delay_frames[i] = NULL;
	args->in_frame      = NULL;
	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
		args->out_frame[i] = NULL;
	args->out_vf_frame  = NULL;
	args->copy_vf       = false;
	args->copy_output   = true;
	args->vf_downscale_log2 = 0;
}

static void start_pipe(
    struct ia_css_pipe *me,
    enum sh_css_pipe_config_override copy_ovrd,
    enum ia_css_input_mode input_mode)
{
	const struct ia_css_coordinate *coord = NULL;
	const struct ia_css_isp_parameters *params = NULL;

#if defined(HAS_NO_INPUT_SYSTEM)
	(void)input_mode;
#endif

	IA_CSS_ENTER_PRIVATE("me = %p, copy_ovrd = %d, input_mode = %d",
			     me, copy_ovrd, input_mode);

	assert(me); /* all callers are in this file and call with non null argument */

	if (!IS_ISP2401) {
		coord = &me->config.internal_frame_origin_bqs_on_sctbl;
		params = me->stream->isp_params_configs;
	}

	sh_css_sp_init_pipeline(&me->pipeline,
				me->mode,
				(uint8_t)ia_css_pipe_get_pipe_num(me),
				me->config.default_capture_config.enable_xnr != 0,
				me->stream->config.pixels_per_clock == 2,
				me->stream->config.continuous,
				false,
				me->required_bds_factor,
				copy_ovrd,
				input_mode,
				&me->stream->config.metadata_config,
				&me->stream->info.metadata_info
#if !defined(HAS_NO_INPUT_SYSTEM)
				, (input_mode == IA_CSS_INPUT_MODE_MEMORY) ?
				(enum mipi_port_id)0 :
				me->stream->config.source.port.port,
#endif
				coord,
				params);

	if (me->config.mode != IA_CSS_PIPE_MODE_COPY) {
		struct ia_css_pipeline_stage *stage;

		stage = me->pipeline.stages;
		if (stage) {
			me->pipeline.current_stage = stage;
			start_binary(me, stage->binary);
		}
	}
	IA_CSS_LEAVE_PRIVATE("void");
}

void
sh_css_invalidate_shading_tables(struct ia_css_stream *stream)
{
	int i;

	assert(stream);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_invalidate_shading_tables() enter:\n");

	for (i = 0; i < stream->num_pipes; i++) {
		assert(stream->pipes[i]);
		sh_css_pipe_free_shading_table(stream->pipes[i]);
	}

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_invalidate_shading_tables() leave: return_void\n");
}

static void
enable_interrupts(enum ia_css_irq_type irq_type)
{
#ifdef USE_INPUT_SYSTEM_VERSION_2
	enum mipi_port_id port;
#endif
	bool enable_pulse = irq_type != IA_CSS_IRQ_TYPE_EDGE;

	IA_CSS_ENTER_PRIVATE("");
	/* Enable IRQ on the SP which signals that SP goes to idle
	 * (aka ready state) */
	cnd_sp_irq_enable(SP0_ID, true);
	/* Set the IRQ device 0 to either level or pulse */
	irq_enable_pulse(IRQ0_ID, enable_pulse);

	cnd_virq_enable_channel(virq_sp, true);

	/* Enable SW interrupt 0, this is used to signal ISYS events */
	cnd_virq_enable_channel(
	    (enum virq_id)(IRQ_SW_CHANNEL0_ID + IRQ_SW_CHANNEL_OFFSET),
	    true);
	/* Enable SW interrupt 1, this is used to signal PSYS events */
	cnd_virq_enable_channel(
	    (enum virq_id)(IRQ_SW_CHANNEL1_ID + IRQ_SW_CHANNEL_OFFSET),
	    true);
#if !defined(HAS_IRQ_MAP_VERSION_2)
	/* IRQ_SW_CHANNEL2_ID does not exist on 240x systems */
	cnd_virq_enable_channel(
	    (enum virq_id)(IRQ_SW_CHANNEL2_ID + IRQ_SW_CHANNEL_OFFSET),
	    true);
	virq_clear_all();
#endif

#ifdef USE_INPUT_SYSTEM_VERSION_2
	for (port = 0; port < N_MIPI_PORT_ID; port++)
		ia_css_isys_rx_enable_all_interrupts(port);
#endif

	IA_CSS_LEAVE_PRIVATE("");
}

static bool sh_css_setup_spctrl_config(const struct ia_css_fw_info *fw,
				       const char *program,
				       ia_css_spctrl_cfg  *spctrl_cfg)
{
	if ((!fw) || (!spctrl_cfg))
		return false;
	spctrl_cfg->sp_entry = 0;
	spctrl_cfg->program_name = (char *)(program);

	spctrl_cfg->ddr_data_offset =  fw->blob.data_source;
	spctrl_cfg->dmem_data_addr = fw->blob.data_target;
	spctrl_cfg->dmem_bss_addr = fw->blob.bss_target;
	spctrl_cfg->data_size = fw->blob.data_size;
	spctrl_cfg->bss_size = fw->blob.bss_size;

	spctrl_cfg->spctrl_config_dmem_addr = fw->info.sp.init_dmem_data;
	spctrl_cfg->spctrl_state_dmem_addr = fw->info.sp.sw_state;

	spctrl_cfg->code_size = fw->blob.size;
	spctrl_cfg->code      = fw->blob.code;
	spctrl_cfg->sp_entry  = fw->info.sp.sp_entry; /* entry function ptr on SP */

	return true;
}

void
ia_css_unload_firmware(void)
{
	if (sh_css_num_binaries) {
		/* we have already loaded before so get rid of the old stuff */
		ia_css_binary_uninit();
		sh_css_unload_firmware();
	}
	fw_explicitly_loaded = false;
}

static void
ia_css_reset_defaults(struct sh_css *css)
{
	struct sh_css default_css;

	/* Reset everything to zero */
	memset(&default_css, 0, sizeof(default_css));

	/* Initialize the non zero values*/
	default_css.check_system_idle = true;
	default_css.num_cont_raw_frames = NUM_CONTINUOUS_FRAMES;

	/* All should be 0: but memset does it already.
	 * default_css.num_mipi_frames[N_CSI_PORTS] = 0;
	 */

	default_css.irq_type = IA_CSS_IRQ_TYPE_EDGE;

	/*Set the defaults to the output */
	*css = default_css;
}

int
ia_css_load_firmware(struct device *dev, const struct ia_css_env *env,
		     const struct ia_css_fw  *fw) {
	int err;

	if (!env)
		return -EINVAL;
	if (!fw)
		return -EINVAL;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_load_firmware() enter\n");

	/* make sure we initialize my_css */
	if (my_css.flush != env->cpu_mem_env.flush)
	{
		ia_css_reset_defaults(&my_css);
		my_css.flush = env->cpu_mem_env.flush;
	}

	ia_css_unload_firmware(); /* in case we are called twice */
	err = sh_css_load_firmware(dev, fw->data, fw->bytes);
	if (!err)
	{
		err = ia_css_binary_init_infos();
		if (!err)
			fw_explicitly_loaded = true;
	}

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_load_firmware() leave\n");
	return err;
}

int
ia_css_init(struct device *dev, const struct ia_css_env *env,
	    const struct ia_css_fw  *fw,
	    u32                 mmu_l1_base,
	    enum ia_css_irq_type     irq_type) {
	int err;
	ia_css_spctrl_cfg spctrl_cfg;

	void (*flush_func)(struct ia_css_acc_fw *fw);
	hrt_data select, enable;

	/*
	 * The C99 standard does not specify the exact object representation of structs;
	 * the representation is compiler dependent.
	 *
	 * The structs that are communicated between host and SP/ISP should have the
	 * exact same object representation. The compiler that is used to compile the
	 * firmware is hivecc.
	 *
	 * To check if a different compiler, used to compile a host application, uses
	 * another object representation, macros are defined specifying the size of
	 * the structs as expected by the firmware.
	 *
	 * A host application shall verify that a sizeof( ) of the struct is equal to
	 * the SIZE_OF_XXX macro of the corresponding struct. If they are not
	 * equal, functionality will break.
	 */
	/* Check struct sh_css_ddr_address_map */
	COMPILATION_ERROR_IF(sizeof(struct sh_css_ddr_address_map)		!= SIZE_OF_SH_CSS_DDR_ADDRESS_MAP_STRUCT);
	/* Check struct host_sp_queues */
	COMPILATION_ERROR_IF(sizeof(struct host_sp_queues)			!= SIZE_OF_HOST_SP_QUEUES_STRUCT);
	COMPILATION_ERROR_IF(sizeof(struct ia_css_circbuf_desc_s)		!= SIZE_OF_IA_CSS_CIRCBUF_DESC_S_STRUCT);
	COMPILATION_ERROR_IF(sizeof(struct ia_css_circbuf_elem_s)		!= SIZE_OF_IA_CSS_CIRCBUF_ELEM_S_STRUCT);

	/* Check struct host_sp_communication */
	COMPILATION_ERROR_IF(sizeof(struct host_sp_communication)		!= SIZE_OF_HOST_SP_COMMUNICATION_STRUCT);
	COMPILATION_ERROR_IF(sizeof(struct sh_css_event_irq_mask)		!= SIZE_OF_SH_CSS_EVENT_IRQ_MASK_STRUCT);

	/* Check struct sh_css_hmm_buffer */
	COMPILATION_ERROR_IF(sizeof(struct sh_css_hmm_buffer)			!= SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT);
	COMPILATION_ERROR_IF(sizeof(struct ia_css_isp_3a_statistics)		!= SIZE_OF_IA_CSS_ISP_3A_STATISTICS_STRUCT);
	COMPILATION_ERROR_IF(sizeof(struct ia_css_isp_dvs_statistics)		!= SIZE_OF_IA_CSS_ISP_DVS_STATISTICS_STRUCT);
	COMPILATION_ERROR_IF(sizeof(struct ia_css_metadata)			!= SIZE_OF_IA_CSS_METADATA_STRUCT);

	/* Check struct ia_css_init_dmem_cfg */
	COMPILATION_ERROR_IF(sizeof(struct ia_css_sp_init_dmem_cfg)		!= SIZE_OF_IA_CSS_SP_INIT_DMEM_CFG_STRUCT);

	if (!fw && !fw_explicitly_loaded)
		return -EINVAL;
	if (!env)
		return -EINVAL;

	sh_css_printf = env->print_env.debug_print;

	IA_CSS_ENTER("void");

	flush_func     = env->cpu_mem_env.flush;

	pipe_global_init();
	ia_css_pipeline_init();
	ia_css_queue_map_init();

	ia_css_device_access_init(&env->hw_access_env);

	select = gpio_reg_load(GPIO0_ID, _gpio_block_reg_do_select)
	& (~GPIO_FLASH_PIN_MASK);
	enable = gpio_reg_load(GPIO0_ID, _gpio_block_reg_do_e)
	| GPIO_FLASH_PIN_MASK;
	sh_css_mmu_set_page_table_base_index(mmu_l1_base);

	my_css_save.mmu_base = mmu_l1_base;

	ia_css_reset_defaults(&my_css);

	my_css_save.driver_env = *env;
	my_css.flush     = flush_func;

	err = ia_css_rmgr_init();
	if (err)
	{
		IA_CSS_LEAVE_ERR(err);
		return err;
	}

	IA_CSS_LOG("init: %d", my_css_save_initialized);

	if (!my_css_save_initialized)
	{
		my_css_save_initialized = true;
		my_css_save.mode = sh_css_mode_working;
		memset(my_css_save.stream_seeds, 0,
		       sizeof(struct sh_css_stream_seed) * MAX_ACTIVE_STREAMS);
		IA_CSS_LOG("init: %d mode=%d", my_css_save_initialized, my_css_save.mode);
	}

	mipi_init();

#ifndef ISP2401
	/* In case this has been programmed already, update internal
	   data structure ... DEPRECATED */
	my_css.page_table_base_index = mmu_get_page_table_base_index(MMU0_ID);

#endif
	my_css.irq_type = irq_type;

	my_css_save.irq_type = irq_type;

	enable_interrupts(my_css.irq_type);

	/* configure GPIO to output mode */
	gpio_reg_store(GPIO0_ID, _gpio_block_reg_do_select, select);
	gpio_reg_store(GPIO0_ID, _gpio_block_reg_do_e, enable);
	gpio_reg_store(GPIO0_ID, _gpio_block_reg_do_0, 0);

	err = ia_css_refcount_init(REFCOUNT_SIZE);
	if (err)
	{
		IA_CSS_LEAVE_ERR(err);
		return err;
	}
	err = sh_css_params_init();
	if (err)
	{
		IA_CSS_LEAVE_ERR(err);
		return err;
	}
	if (fw)
	{
		ia_css_unload_firmware(); /* in case we already had firmware loaded */
		err = sh_css_load_firmware(dev, fw->data, fw->bytes);
		if (err) {
			IA_CSS_LEAVE_ERR(err);
			return err;
		}
		err = ia_css_binary_init_infos();
		if (err) {
			IA_CSS_LEAVE_ERR(err);
			return err;
		}
		fw_explicitly_loaded = false;
#ifndef ISP2401
		my_css_save.loaded_fw = (struct ia_css_fw *)fw;
#endif
	}
	if (!sh_css_setup_spctrl_config(&sh_css_sp_fw, SP_PROG_NAME, &spctrl_cfg))
		return -EINVAL;

	err = ia_css_spctrl_load_fw(SP0_ID, &spctrl_cfg);
	if (err)
	{
		IA_CSS_LEAVE_ERR(err);
		return err;
	}

#if WITH_PC_MONITORING
	if (!thread_alive)
	{
		thread_alive++;
		sh_css_print("PC_MONITORING: %s() -- create thread DISABLED\n",
			     __func__);
		spying_thread_create();
	}
#endif
	if (!sh_css_hrt_system_is_idle())
	{
		IA_CSS_LEAVE_ERR(-EBUSY);
		return -EBUSY;
	}
	/* can be called here, queuing works, but:
	   - when sp is started later, it will wipe queued items
	   so for now we leave it for later and make sure
	   updates are not called to frequently.
	sh_css_init_buffer_queues();
	*/

#if defined(HAS_INPUT_SYSTEM_VERSION_2) && defined(HAS_INPUT_SYSTEM_VERSION_2401)
#if	defined(USE_INPUT_SYSTEM_VERSION_2)
	gp_device_reg_store(GP_DEVICE0_ID, _REG_GP_SWITCH_ISYS2401_ADDR, 0);
#elif defined(USE_INPUT_SYSTEM_VERSION_2401)
	gp_device_reg_store(GP_DEVICE0_ID, _REG_GP_SWITCH_ISYS2401_ADDR, 1);
#endif
#endif

#if !defined(HAS_NO_INPUT_SYSTEM)

	if (!IS_ISP2401)
		dma_set_max_burst_size(DMA0_ID, HIVE_DMA_BUS_DDR_CONN,
				       ISP2400_DMA_MAX_BURST_LENGTH);
	else
		dma_set_max_burst_size(DMA0_ID, HIVE_DMA_BUS_DDR_CONN,
				       ISP2401_DMA_MAX_BURST_LENGTH);

	if (ia_css_isys_init() != INPUT_SYSTEM_ERR_NO_ERROR)
		err = -EINVAL;
#endif

	sh_css_params_map_and_store_default_gdc_lut();

	IA_CSS_LEAVE_ERR(err);
	return err;
}

int
ia_css_enable_isys_event_queue(bool enable) {
	if (sh_css_sp_is_running())
		return -EBUSY;
	sh_css_sp_enable_isys_event_queue(enable);
	return 0;
}

/* For Acceleration API: Flush FW (shared buffer pointer) arguments */
void
sh_css_flush(struct ia_css_acc_fw *fw)
{
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_flush() enter:\n");
	if ((fw) && (my_css.flush))
		my_css.flush(fw);
}

/* Mapping sp threads. Currently, this is done when a stream is created and
 * pipelines are ready to be converted to sp pipelines. Be careful if you are
 * doing it from stream_create since we could run out of sp threads due to
 * allocation on inactive pipelines. */
static int
map_sp_threads(struct ia_css_stream *stream, bool map) {
	struct ia_css_pipe *main_pipe = NULL;
	struct ia_css_pipe *copy_pipe = NULL;
	struct ia_css_pipe *capture_pipe = NULL;
	struct ia_css_pipe *acc_pipe = NULL;
	int err = 0;
	enum ia_css_pipe_id pipe_id;

	assert(stream);
	IA_CSS_ENTER_PRIVATE("stream = %p, map = %s",
			     stream, map ? "true" : "false");

	if (!stream)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	main_pipe = stream->last_pipe;
	pipe_id	= main_pipe->mode;

	ia_css_pipeline_map(main_pipe->pipe_num, map);

	switch (pipe_id)
	{
	case IA_CSS_PIPE_ID_PREVIEW:
		copy_pipe    = main_pipe->pipe_settings.preview.copy_pipe;
		capture_pipe = main_pipe->pipe_settings.preview.capture_pipe;
		acc_pipe     = main_pipe->pipe_settings.preview.acc_pipe;
		break;

	case IA_CSS_PIPE_ID_VIDEO:
		copy_pipe    = main_pipe->pipe_settings.video.copy_pipe;
		capture_pipe = main_pipe->pipe_settings.video.capture_pipe;
		break;

	case IA_CSS_PIPE_ID_CAPTURE:
	case IA_CSS_PIPE_ID_ACC:
	default:
		break;
	}

	if (acc_pipe)
	{
		ia_css_pipeline_map(acc_pipe->pipe_num, map);
	}

	if (capture_pipe)
	{
		ia_css_pipeline_map(capture_pipe->pipe_num, map);
	}

	/* Firmware expects copy pipe to be the last pipe mapped. (if needed) */
	if (copy_pipe)
	{
		ia_css_pipeline_map(copy_pipe->pipe_num, map);
	}
	/* DH regular multi pipe - not continuous mode: map the next pipes too */
	if (!stream->config.continuous)
	{
		int i;

		for (i = 1; i < stream->num_pipes; i++)
			ia_css_pipeline_map(stream->pipes[i]->pipe_num, map);
	}

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/* creates a host pipeline skeleton for all pipes in a stream. Called during
 * stream_create. */
static int
create_host_pipeline_structure(struct ia_css_stream *stream) {
	struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL;
	struct ia_css_pipe *acc_pipe = NULL;
	enum ia_css_pipe_id pipe_id;
	struct ia_css_pipe *main_pipe = NULL;
	int err = 0;
	unsigned int copy_pipe_delay = 0,
	capture_pipe_delay = 0;

	assert(stream);
	IA_CSS_ENTER_PRIVATE("stream = %p", stream);

	if (!stream)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	main_pipe	= stream->last_pipe;
	assert(main_pipe);
	if (!main_pipe)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	pipe_id	= main_pipe->mode;

	switch (pipe_id)
	{
	case IA_CSS_PIPE_ID_PREVIEW:
		copy_pipe    = main_pipe->pipe_settings.preview.copy_pipe;
		copy_pipe_delay = main_pipe->dvs_frame_delay;
		capture_pipe = main_pipe->pipe_settings.preview.capture_pipe;
		capture_pipe_delay = IA_CSS_FRAME_DELAY_0;
		acc_pipe     = main_pipe->pipe_settings.preview.acc_pipe;
		err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode,
					     main_pipe->pipe_num, main_pipe->dvs_frame_delay);
		break;

	case IA_CSS_PIPE_ID_VIDEO:
		copy_pipe    = main_pipe->pipe_settings.video.copy_pipe;
		copy_pipe_delay = main_pipe->dvs_frame_delay;
		capture_pipe = main_pipe->pipe_settings.video.capture_pipe;
		capture_pipe_delay = IA_CSS_FRAME_DELAY_0;
		err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode,
					     main_pipe->pipe_num, main_pipe->dvs_frame_delay);
		break;

	case IA_CSS_PIPE_ID_CAPTURE:
		capture_pipe = main_pipe;
		capture_pipe_delay = main_pipe->dvs_frame_delay;
		break;

	case IA_CSS_PIPE_ID_YUVPP:
		err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode,
					     main_pipe->pipe_num, main_pipe->dvs_frame_delay);
		break;

	case IA_CSS_PIPE_ID_ACC:
		err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode,
					     main_pipe->pipe_num, main_pipe->dvs_frame_delay);
		break;

	default:
		err = -EINVAL;
	}

	if (!(err) && copy_pipe)
	{
		err = ia_css_pipeline_create(&copy_pipe->pipeline,
					     copy_pipe->mode,
					     copy_pipe->pipe_num,
					     copy_pipe_delay);
	}

	if (!(err) && capture_pipe)
	{
		err = ia_css_pipeline_create(&capture_pipe->pipeline,
					     capture_pipe->mode,
					     capture_pipe->pipe_num,
					     capture_pipe_delay);
	}

	if (!(err) && acc_pipe)
	{
		err = ia_css_pipeline_create(&acc_pipe->pipeline, acc_pipe->mode,
					     acc_pipe->pipe_num, main_pipe->dvs_frame_delay);
	}

	/* DH regular multi pipe - not continuous mode: create the next pipelines too */
	if (!stream->config.continuous)
	{
		int i;

		for (i = 1; i < stream->num_pipes && 0 == err; i++) {
			main_pipe = stream->pipes[i];
			err = ia_css_pipeline_create(&main_pipe->pipeline,
						     main_pipe->mode,
						     main_pipe->pipe_num,
						     main_pipe->dvs_frame_delay);
		}
	}

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/* creates a host pipeline for all pipes in a stream. Called during
 * stream_start. */
static int
create_host_pipeline(struct ia_css_stream *stream) {
	struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL;
	struct ia_css_pipe *acc_pipe = NULL;
	enum ia_css_pipe_id pipe_id;
	struct ia_css_pipe *main_pipe = NULL;
	int err = 0;
	unsigned int max_input_width = 0;

	IA_CSS_ENTER_PRIVATE("stream = %p", stream);
	if (!stream)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	main_pipe	= stream->last_pipe;
	pipe_id	= main_pipe->mode;

	/* No continuous frame allocation for capture pipe. It uses the
	 * "main" pipe's frames. */
	if ((pipe_id == IA_CSS_PIPE_ID_PREVIEW) ||
	    (pipe_id == IA_CSS_PIPE_ID_VIDEO))
	{
		/* About pipe_id == IA_CSS_PIPE_ID_PREVIEW && stream->config.mode != IA_CSS_INPUT_MODE_MEMORY:
		 * The original condition pipe_id == IA_CSS_PIPE_ID_PREVIEW is too strong. E.g. in SkyCam (with memory
		 * based input frames) there is no continuous mode and thus no need for allocated continuous frames
		 * This is not only for SkyCam but for all preview cases that use DDR based input frames. For this
		 * reason the stream->config.mode != IA_CSS_INPUT_MODE_MEMORY has beed added.
		 */
		if (stream->config.continuous ||
		    (pipe_id == IA_CSS_PIPE_ID_PREVIEW &&
		     stream->config.mode != IA_CSS_INPUT_MODE_MEMORY)) {
			err = alloc_continuous_frames(main_pipe, true);
			if (err)
				goto ERR;
		}
	}

#if defined(USE_INPUT_SYSTEM_VERSION_2)
	/* old isys: need to allocate_mipi_frames() even in IA_CSS_PIPE_MODE_COPY */
	if (pipe_id != IA_CSS_PIPE_ID_ACC)
	{
		err = allocate_mipi_frames(main_pipe, &stream->info);
		if (err)
			goto ERR;
	}
#elif defined(USE_INPUT_SYSTEM_VERSION_2401)
	if ((pipe_id != IA_CSS_PIPE_ID_ACC) &&
	    (main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY))
	{
		err = allocate_mipi_frames(main_pipe, &stream->info);
		if (err)
			goto ERR;
	}
#endif

	switch (pipe_id)
	{
	case IA_CSS_PIPE_ID_PREVIEW:
		copy_pipe    = main_pipe->pipe_settings.preview.copy_pipe;
		capture_pipe = main_pipe->pipe_settings.preview.capture_pipe;
		acc_pipe     = main_pipe->pipe_settings.preview.acc_pipe;
		max_input_width =
		    main_pipe->pipe_settings.preview.preview_binary.info->sp.input.max_width;

		err = create_host_preview_pipeline(main_pipe);
		if (err)
			goto ERR;

		break;

	case IA_CSS_PIPE_ID_VIDEO:
		copy_pipe    = main_pipe->pipe_settings.video.copy_pipe;
		capture_pipe = main_pipe->pipe_settings.video.capture_pipe;
		max_input_width =
		    main_pipe->pipe_settings.video.video_binary.info->sp.input.max_width;

		err = create_host_video_pipeline(main_pipe);
		if (err)
			goto ERR;

		break;

	case IA_CSS_PIPE_ID_CAPTURE:
		capture_pipe = main_pipe;

		break;

	case IA_CSS_PIPE_ID_YUVPP:
		err = create_host_yuvpp_pipeline(main_pipe);
		if (err)
			goto ERR;

		break;

	case IA_CSS_PIPE_ID_ACC:
		err = create_host_acc_pipeline(main_pipe);
		if (err)
			goto ERR;

		break;
	default:
		err = -EINVAL;
	}
	if (err)
		goto ERR;

	if (copy_pipe)
	{
		err = create_host_copy_pipeline(copy_pipe, max_input_width,
						main_pipe->continuous_frames[0]);
		if (err)
			goto ERR;
	}

	if (capture_pipe)
	{
		err = create_host_capture_pipeline(capture_pipe);
		if (err)
			goto ERR;
	}

	if (acc_pipe)
	{
		err = create_host_acc_pipeline(acc_pipe);
		if (err)
			goto ERR;
	}

	/* DH regular multi pipe - not continuous mode: create the next pipelines too */
	if (!stream->config.continuous)
	{
		int i;

		for (i = 1; i < stream->num_pipes && 0 == err; i++) {
			switch (stream->pipes[i]->mode) {
			case IA_CSS_PIPE_ID_PREVIEW:
				err = create_host_preview_pipeline(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_VIDEO:
				err = create_host_video_pipeline(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_CAPTURE:
				err = create_host_capture_pipeline(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_YUVPP:
				err = create_host_yuvpp_pipeline(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_ACC:
				err = create_host_acc_pipeline(stream->pipes[i]);
				break;
			default:
				err = -EINVAL;
			}
			if (err)
				goto ERR;
		}
	}

ERR:
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static const struct ia_css_pipe default_pipe = IA_CSS_DEFAULT_PIPE;
static const struct ia_css_preview_settings preview = IA_CSS_DEFAULT_PREVIEW_SETTINGS;
static const struct ia_css_capture_settings capture = IA_CSS_DEFAULT_CAPTURE_SETTINGS;
static const struct ia_css_video_settings video = IA_CSS_DEFAULT_VIDEO_SETTINGS;
static const struct ia_css_yuvpp_settings yuvpp = IA_CSS_DEFAULT_YUVPP_SETTINGS;

static int
init_pipe_defaults(enum ia_css_pipe_mode mode,
		   struct ia_css_pipe *pipe,
		   bool copy_pipe) {

	if (!pipe)
	{
		IA_CSS_ERROR("NULL pipe parameter");
		return -EINVAL;
	}

	/* Initialize pipe to pre-defined defaults */
	memcpy(pipe, &default_pipe, sizeof(default_pipe));

	/* TODO: JB should not be needed, but temporary backward reference */
	switch (mode)
	{
	case IA_CSS_PIPE_MODE_PREVIEW:
		pipe->mode = IA_CSS_PIPE_ID_PREVIEW;
		memcpy(&pipe->pipe_settings.preview, &preview, sizeof(preview));
		break;
	case IA_CSS_PIPE_MODE_CAPTURE:
		if (copy_pipe) {
			pipe->mode = IA_CSS_PIPE_ID_COPY;
		} else {
			pipe->mode = IA_CSS_PIPE_ID_CAPTURE;
		}
		memcpy(&pipe->pipe_settings.capture, &capture, sizeof(capture));
		break;
	case IA_CSS_PIPE_MODE_VIDEO:
		pipe->mode = IA_CSS_PIPE_ID_VIDEO;
		memcpy(&pipe->pipe_settings.video, &video, sizeof(video));
		break;
	case IA_CSS_PIPE_MODE_ACC:
		pipe->mode = IA_CSS_PIPE_ID_ACC;
		break;
	case IA_CSS_PIPE_MODE_COPY:
		pipe->mode = IA_CSS_PIPE_ID_CAPTURE;
		break;
	case IA_CSS_PIPE_MODE_YUVPP:
		pipe->mode = IA_CSS_PIPE_ID_YUVPP;
		memcpy(&pipe->pipe_settings.yuvpp, &yuvpp, sizeof(yuvpp));
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static void
pipe_global_init(void)
{
	u8 i;

	my_css.pipe_counter = 0;
	for (i = 0; i < IA_CSS_PIPELINE_NUM_MAX; i++) {
		my_css.all_pipes[i] = NULL;
	}
}

static int
pipe_generate_pipe_num(const struct ia_css_pipe *pipe,
		       unsigned int *pipe_number) {
	const u8 INVALID_PIPE_NUM = (uint8_t)~(0);
	u8 pipe_num = INVALID_PIPE_NUM;
	u8 i;

	if (!pipe)
	{
		IA_CSS_ERROR("NULL pipe parameter");
		return -EINVAL;
	}

	/* Assign a new pipe_num .... search for empty place */
	for (i = 0; i < IA_CSS_PIPELINE_NUM_MAX; i++)
	{
		if (!my_css.all_pipes[i]) {
			/*position is reserved */
			my_css.all_pipes[i] = (struct ia_css_pipe *)pipe;
			pipe_num = i;
			break;
		}
	}
	if (pipe_num == INVALID_PIPE_NUM)
	{
		/* Max number of pipes already allocated */
		IA_CSS_ERROR("Max number of pipes already created");
		return -ENOSPC;
	}

	my_css.pipe_counter++;

	IA_CSS_LOG("pipe_num (%d)", pipe_num);

	*pipe_number = pipe_num;
	return 0;
}

static void
pipe_release_pipe_num(unsigned int pipe_num)
{
	my_css.all_pipes[pipe_num] = NULL;
	my_css.pipe_counter--;
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "pipe_release_pipe_num (%d)\n", pipe_num);
}

static int
create_pipe(enum ia_css_pipe_mode mode,
	    struct ia_css_pipe **pipe,
	    bool copy_pipe) {
	int err = 0;
	struct ia_css_pipe *me;

	if (!pipe)
	{
		IA_CSS_ERROR("NULL pipe parameter");
		return -EINVAL;
	}

	me = kmalloc(sizeof(*me), GFP_KERNEL);
	if (!me)
		return -ENOMEM;

	err = init_pipe_defaults(mode, me, copy_pipe);
	if (err)
	{
		kfree(me);
		return err;
	}

	err = pipe_generate_pipe_num(me, &me->pipe_num);
	if (err)
	{
		kfree(me);
		return err;
	}

	*pipe = me;
	return 0;
}

struct ia_css_pipe *
find_pipe_by_num(uint32_t pipe_num)
{
	unsigned int i;

	for (i = 0; i < IA_CSS_PIPELINE_NUM_MAX; i++) {
		if (my_css.all_pipes[i] &&
		    ia_css_pipe_get_pipe_num(my_css.all_pipes[i]) == pipe_num) {
			return my_css.all_pipes[i];
		}
	}
	return NULL;
}

static void sh_css_pipe_free_acc_binaries(
    struct ia_css_pipe *pipe)
{
	struct ia_css_pipeline *pipeline;
	struct ia_css_pipeline_stage *stage;

	assert(pipe);
	if (!pipe) {
		IA_CSS_ERROR("NULL input pointer");
		return;
	}
	pipeline = &pipe->pipeline;

	/* loop through the stages and unload them */
	for (stage = pipeline->stages; stage; stage = stage->next) {
		struct ia_css_fw_info *firmware = (struct ia_css_fw_info *)
						  stage->firmware;
		if (firmware)
			ia_css_pipe_unload_extension(pipe, firmware);
	}
}

int
ia_css_pipe_destroy(struct ia_css_pipe *pipe) {
	int err = 0;

	IA_CSS_ENTER("pipe = %p", pipe);

	if (!pipe)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	if (pipe->stream)
	{
		IA_CSS_LOG("ia_css_stream_destroy not called!");
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	switch (pipe->config.mode)
	{
	case IA_CSS_PIPE_MODE_PREVIEW:
		/* need to take into account that this function is also called
		   on the internal copy pipe */
		if (pipe->mode == IA_CSS_PIPE_ID_PREVIEW) {
			ia_css_frame_free_multiple(NUM_CONTINUOUS_FRAMES,
						   pipe->continuous_frames);
			ia_css_metadata_free_multiple(NUM_CONTINUOUS_FRAMES,
						      pipe->cont_md_buffers);
			if (pipe->pipe_settings.preview.copy_pipe) {
				err = ia_css_pipe_destroy(pipe->pipe_settings.preview.copy_pipe);
				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
						    "ia_css_pipe_destroy(): destroyed internal copy pipe err=%d\n",
						    err);
			}
		}
		break;
	case IA_CSS_PIPE_MODE_VIDEO:
		if (pipe->mode == IA_CSS_PIPE_ID_VIDEO) {
			ia_css_frame_free_multiple(NUM_CONTINUOUS_FRAMES,
						   pipe->continuous_frames);
			ia_css_metadata_free_multiple(NUM_CONTINUOUS_FRAMES,
						      pipe->cont_md_buffers);
			if (pipe->pipe_settings.video.copy_pipe) {
				err = ia_css_pipe_destroy(pipe->pipe_settings.video.copy_pipe);
				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
						    "ia_css_pipe_destroy(): destroyed internal copy pipe err=%d\n",
						    err);
			}
		}
#ifndef ISP2401
		ia_css_frame_free_multiple(NUM_TNR_FRAMES,
					   pipe->pipe_settings.video.tnr_frames);
#else
		ia_css_frame_free_multiple(NUM_TNR_FRAMES,
					   pipe->pipe_settings.video.tnr_frames);
#endif
		ia_css_frame_free_multiple(MAX_NUM_VIDEO_DELAY_FRAMES,
					   pipe->pipe_settings.video.delay_frames);
		break;
	case IA_CSS_PIPE_MODE_CAPTURE:
		ia_css_frame_free_multiple(MAX_NUM_VIDEO_DELAY_FRAMES,
					   pipe->pipe_settings.capture.delay_frames);
		break;
	case IA_CSS_PIPE_MODE_ACC:
		sh_css_pipe_free_acc_binaries(pipe);
		break;
	case IA_CSS_PIPE_MODE_COPY:
		break;
	case IA_CSS_PIPE_MODE_YUVPP:
		break;
	}

	sh_css_params_free_gdc_lut(pipe->scaler_pp_lut);
	pipe->scaler_pp_lut = mmgr_NULL;

	my_css.active_pipes[ia_css_pipe_get_pipe_num(pipe)] = NULL;
	sh_css_pipe_free_shading_table(pipe);

	ia_css_pipeline_destroy(&pipe->pipeline);
	pipe_release_pipe_num(ia_css_pipe_get_pipe_num(pipe));

	/* Temporarily, not every sh_css_pipe has an acc_extension. */
	if (pipe->config.acc_extension)
	{
		ia_css_pipe_unload_extension(pipe, pipe->config.acc_extension);
	}
	kfree(pipe);
	IA_CSS_LEAVE("err = %d", err);
	return err;
}

void
ia_css_uninit(void)
{
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_uninit() enter: void\n");
#if WITH_PC_MONITORING
	sh_css_print("PC_MONITORING: %s() -- started\n", __func__);
	print_pc_histogram();
#endif

	sh_css_params_free_default_gdc_lut();

	/* TODO: JB: implement decent check and handling of freeing mipi frames */
	//assert(ref_count_mipi_allocation == 0); //mipi frames are not freed
	/* cleanup generic data */
	sh_css_params_uninit();
	ia_css_refcount_uninit();

	ia_css_rmgr_uninit();

#if !defined(HAS_NO_INPUT_FORMATTER)
	/* needed for reprogramming the inputformatter after power cycle of css */
	ifmtr_set_if_blocking_mode_reset = true;
#endif

	if (!fw_explicitly_loaded) {
		ia_css_unload_firmware();
	}
	ia_css_spctrl_unload_fw(SP0_ID);
	sh_css_sp_set_sp_running(false);
#if defined(USE_INPUT_SYSTEM_VERSION_2) || defined(USE_INPUT_SYSTEM_VERSION_2401)
	/* check and free any remaining mipi frames */
	free_mipi_frames(NULL);
#endif

	sh_css_sp_reset_global_vars();

#if !defined(HAS_NO_INPUT_SYSTEM)
	ia_css_isys_uninit();
#endif

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_uninit() leave: return_void\n");
}

#if defined(HAS_IRQ_MAP_VERSION_2)
int ia_css_irq_translate(
    unsigned int *irq_infos)
{
	enum virq_id	irq;
	enum hrt_isp_css_irq_status status = hrt_isp_css_irq_status_more_irqs;
	unsigned int infos = 0;

	/* irq_infos can be NULL, but that would make the function useless */
	/* assert(irq_infos != NULL); */
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "ia_css_irq_translate() enter: irq_infos=%p\n", irq_infos);

	while (status == hrt_isp_css_irq_status_more_irqs) {
		status = virq_get_channel_id(&irq);
		if (status == hrt_isp_css_irq_status_error)
			return -EINVAL;

#if WITH_PC_MONITORING
		sh_css_print("PC_MONITORING: %s() irq = %d, sh_binary_running set to 0\n",
			     __func__, irq);
		sh_binary_running = 0;
#endif

		switch (irq) {
		case virq_sp:
			/* When SP goes to idle, info is available in the
			 * event queue. */
			infos |= IA_CSS_IRQ_INFO_EVENTS_READY;
			break;
		case virq_isp:
			break;
#if !defined(HAS_NO_INPUT_SYSTEM)
		case virq_isys_sof:
			infos |= IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF;
			break;
		case virq_isys_eof:
			infos |= IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF;
			break;
		case virq_isys_csi:
			infos |= IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR;
			break;
#endif
#if !defined(HAS_NO_INPUT_FORMATTER)
		case virq_ifmt0_id:
			infos |= IA_CSS_IRQ_INFO_IF_ERROR;
			break;
#endif
		case virq_dma:
			infos |= IA_CSS_IRQ_INFO_DMA_ERROR;
			break;
		case virq_sw_pin_0:
			infos |= sh_css_get_sw_interrupt_value(0);
			break;
		case virq_sw_pin_1:
			infos |= sh_css_get_sw_interrupt_value(1);
			/* pqiao TODO: also assumption here */
			break;
		default:
			break;
		}
	}

	if (irq_infos)
		*irq_infos = infos;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "ia_css_irq_translate() leave: irq_infos=%u\n",
			    infos);

	return 0;
}

int ia_css_irq_enable(
    enum ia_css_irq_info info,
    bool enable)
{
	enum virq_id	irq = N_virq_id;

	IA_CSS_ENTER("info=%d, enable=%d", info, enable);

	switch (info) {
#if !defined(HAS_NO_INPUT_FORMATTER)
	case IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF:
		irq = virq_isys_sof;
		break;
	case IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF:
		irq = virq_isys_eof;
		break;
	case IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR:
		irq = virq_isys_csi;
		break;
	case IA_CSS_IRQ_INFO_IF_ERROR:
		irq = virq_ifmt0_id;
		break;
#else
	case IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF:
	case IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF:
	case IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR:
	case IA_CSS_IRQ_INFO_IF_ERROR:
		/* Just ignore those unused IRQs without printing errors */
		return 0;
#endif
	case IA_CSS_IRQ_INFO_DMA_ERROR:
		irq = virq_dma;
		break;
	case IA_CSS_IRQ_INFO_SW_0:
		irq = virq_sw_pin_0;
		break;
	case IA_CSS_IRQ_INFO_SW_1:
		irq = virq_sw_pin_1;
		break;
	default:
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	cnd_virq_enable_channel(irq, enable);

	IA_CSS_LEAVE_ERR(0);
	return 0;
}

#else
#error "sh_css.c: IRQ MAP must be one of { IRQ_MAP_VERSION_2 }"
#endif

static unsigned int
sh_css_get_sw_interrupt_value(unsigned int irq)
{
	unsigned int irq_value;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_get_sw_interrupt_value() enter: irq=%d\n", irq);
	irq_value = sh_css_sp_get_sw_interrupt_value(irq);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_get_sw_interrupt_value() leave: irq_value=%d\n", irq_value);
	return irq_value;
}

/* configure and load the copy binary, the next binary is used to
   determine whether the copy binary needs to do left padding. */
static int load_copy_binary(
    struct ia_css_pipe *pipe,
    struct ia_css_binary *copy_binary,
    struct ia_css_binary *next_binary)
{
	struct ia_css_frame_info copy_out_info, copy_in_info, copy_vf_info;
	unsigned int left_padding;
	int err;
	struct ia_css_binary_descr copy_descr;

	/* next_binary can be NULL */
	assert(pipe);
	assert(copy_binary);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "load_copy_binary() enter:\n");

	if (next_binary) {
		copy_out_info = next_binary->in_frame_info;
		left_padding = next_binary->left_padding;
	} else {
		copy_out_info = pipe->output_info[0];
		copy_vf_info = pipe->vf_output_info[0];
		ia_css_frame_info_set_format(&copy_vf_info, IA_CSS_FRAME_FORMAT_YUV_LINE);
		left_padding = 0;
	}

	ia_css_pipe_get_copy_binarydesc(pipe, &copy_descr,
					&copy_in_info, &copy_out_info,
					(next_binary) ? NULL : NULL/*TODO: &copy_vf_info*/);
	err = ia_css_binary_find(&copy_descr, copy_binary);
	if (err)
		return err;
	copy_binary->left_padding = left_padding;
	return 0;
}

static int
alloc_continuous_frames(
    struct ia_css_pipe *pipe, bool init_time) {
	int err = 0;
	struct ia_css_frame_info ref_info;
	enum ia_css_pipe_id pipe_id;
	bool continuous;
	unsigned int i, idx;
	unsigned int num_frames;
	struct ia_css_pipe *capture_pipe = NULL;

	IA_CSS_ENTER_PRIVATE("pipe = %p, init_time = %d", pipe, init_time);

	if ((!pipe) || (!pipe->stream))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	pipe_id = pipe->mode;
	continuous = pipe->stream->config.continuous;

	if (continuous)
	{
		if (init_time) {
			num_frames = pipe->stream->config.init_num_cont_raw_buf;
			pipe->stream->continuous_pipe = pipe;
		} else
			num_frames = pipe->stream->config.target_num_cont_raw_buf;
	} else
	{
		num_frames = NUM_ONLINE_INIT_CONTINUOUS_FRAMES;
	}

	if (pipe_id == IA_CSS_PIPE_ID_PREVIEW)
	{
		ref_info = pipe->pipe_settings.preview.preview_binary.in_frame_info;
	} else if (pipe_id == IA_CSS_PIPE_ID_VIDEO)
	{
		ref_info = pipe->pipe_settings.video.video_binary.in_frame_info;
	} else
	{
		/* should not happen */
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

#if defined(USE_INPUT_SYSTEM_VERSION_2401)
	/* For CSI2+, the continuous frame will hold the full input frame */
	ref_info.res.width = pipe->stream->config.input_config.input_res.width;
	ref_info.res.height = pipe->stream->config.input_config.input_res.height;

	/* Ensure padded width is aligned for 2401 */
	ref_info.padded_width = CEIL_MUL(ref_info.res.width, 2 * ISP_VEC_NELEMS);
#endif

#if !defined(HAS_NO_PACKED_RAW_PIXELS)
	if (pipe->stream->config.pack_raw_pixels)
	{
		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
				    "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW_PACKED\n");
		ref_info.format = IA_CSS_FRAME_FORMAT_RAW_PACKED;
	} else
#endif
	{
		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
				    "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW\n");
		ref_info.format = IA_CSS_FRAME_FORMAT_RAW;
	}

	/* Write format back to binary */
	if (pipe_id == IA_CSS_PIPE_ID_PREVIEW)
	{
		pipe->pipe_settings.preview.preview_binary.in_frame_info.format =
		    ref_info.format;
		capture_pipe = pipe->pipe_settings.preview.capture_pipe;
	} else if (pipe_id == IA_CSS_PIPE_ID_VIDEO)
	{
		pipe->pipe_settings.video.video_binary.in_frame_info.format = ref_info.format;
		capture_pipe = pipe->pipe_settings.video.capture_pipe;
	} else
	{
		/* should not happen */
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	if (init_time)
		idx = 0;
	else
		idx = pipe->stream->config.init_num_cont_raw_buf;

	for (i = idx; i < NUM_CONTINUOUS_FRAMES; i++)
	{
		/* free previous frame */
		if (pipe->continuous_frames[i]) {
			ia_css_frame_free(pipe->continuous_frames[i]);
			pipe->continuous_frames[i] = NULL;
		}
		/* free previous metadata buffer */
		ia_css_metadata_free(pipe->cont_md_buffers[i]);
		pipe->cont_md_buffers[i] = NULL;

		/* check if new frame needed */
		if (i < num_frames) {
			/* allocate new frame */
			err = ia_css_frame_allocate_from_info(
				  &pipe->continuous_frames[i],
				  &ref_info);
			if (err) {
				IA_CSS_LEAVE_ERR_PRIVATE(err);
				return err;
			}
			/* allocate metadata buffer */
			pipe->cont_md_buffers[i] = ia_css_metadata_allocate(
						       &pipe->stream->info.metadata_info);
		}
	}
	IA_CSS_LEAVE_ERR_PRIVATE(0);
	return 0;
}

int
ia_css_alloc_continuous_frame_remain(struct ia_css_stream *stream) {
	if (!stream)
		return -EINVAL;
	return alloc_continuous_frames(stream->continuous_pipe, false);
}

static int
load_preview_binaries(struct ia_css_pipe *pipe) {
	struct ia_css_frame_info prev_in_info,
		prev_bds_out_info,
		prev_out_info,
		prev_vf_info;
	struct ia_css_binary_descr preview_descr;
	bool online;
	int err = 0;
	bool continuous, need_vf_pp = false;
	bool need_isp_copy_binary = false;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	bool sensor = false;
#endif
	/* preview only have 1 output pin now */
	struct ia_css_frame_info *pipe_out_info = &pipe->output_info[0];
	struct ia_css_preview_settings *mycs  = &pipe->pipe_settings.preview;

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->stream);
	assert(pipe->mode == IA_CSS_PIPE_ID_PREVIEW);

	online = pipe->stream->config.online;
	continuous = pipe->stream->config.continuous;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR;
#endif

	if (mycs->preview_binary.info)
		return 0;

	err = ia_css_util_check_input(&pipe->stream->config, false, false);
	if (err)
		return err;
	err = ia_css_frame_check_info(pipe_out_info);
	if (err)
		return err;

	/* Note: the current selection of vf_pp binary and
	 * parameterization of the preview binary contains a few pieces
	 * of hardcoded knowledge. This needs to be cleaned up such that
	 * the binary selection becomes more generic.
	 * The vf_pp binary is needed if one or more of the following features
	 * are required:
	 * 1. YUV downscaling.
	 * 2. Digital zoom.
	 * 3. An output format that is not supported by the preview binary.
	 *    In practice this means something other than yuv_line or nv12.
	 * The decision if the vf_pp binary is needed for YUV downscaling is
	 * made after the preview binary selection, since some preview binaries
	 * can perform the requested YUV downscaling.
	 * */
	need_vf_pp = pipe->config.enable_dz;
	need_vf_pp |= pipe_out_info->format != IA_CSS_FRAME_FORMAT_YUV_LINE &&
	!(pipe_out_info->format == IA_CSS_FRAME_FORMAT_NV12 ||
	  pipe_out_info->format == IA_CSS_FRAME_FORMAT_NV12_16 ||
	  pipe_out_info->format == IA_CSS_FRAME_FORMAT_NV12_TILEY);

	/* Preview step 1 */
	if (pipe->vf_yuv_ds_input_info.res.width)
		prev_vf_info = pipe->vf_yuv_ds_input_info;
	else
		prev_vf_info = *pipe_out_info;
	/* If vf_pp is needed, then preview must output yuv_line.
	 * The exception is when vf_pp is manually disabled, that is only
	 * used in combination with a pipeline extension that requires
	 * yuv_line as input.
	 * */
	if (need_vf_pp)
		ia_css_frame_info_set_format(&prev_vf_info,
					     IA_CSS_FRAME_FORMAT_YUV_LINE);

	err = ia_css_pipe_get_preview_binarydesc(
	    pipe,
	    &preview_descr,
	    &prev_in_info,
	    &prev_bds_out_info,
	    &prev_out_info,
	    &prev_vf_info);
	if (err)
		return err;
	err = ia_css_binary_find(&preview_descr, &mycs->preview_binary);
	if (err)
		return err;

	if (IS_ISP2401) {
		/* The delay latency determines the number of invalid frames after
		* a stream is started. */
		pipe->num_invalid_frames = pipe->dvs_frame_delay;
		pipe->info.num_invalid_frames = pipe->num_invalid_frames;

		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
				    "load_preview_binaries() num_invalid_frames=%d dvs_frame_delay=%d\n",
				    pipe->num_invalid_frames, pipe->dvs_frame_delay);
	}

	/* The vf_pp binary is needed when (further) YUV downscaling is required */
	need_vf_pp |= mycs->preview_binary.out_frame_info[0].res.width != pipe_out_info->res.width;
	need_vf_pp |= mycs->preview_binary.out_frame_info[0].res.height != pipe_out_info->res.height;

	/* When vf_pp is needed, then the output format of the selected
	 * preview binary must be yuv_line. If this is not the case,
	 * then the preview binary selection is done again.
	 */
	if (need_vf_pp &&
	    (mycs->preview_binary.out_frame_info[0].format != IA_CSS_FRAME_FORMAT_YUV_LINE))
	{
		/* Preview step 2 */
		if (pipe->vf_yuv_ds_input_info.res.width)
			prev_vf_info = pipe->vf_yuv_ds_input_info;
		else
			prev_vf_info = *pipe_out_info;

		ia_css_frame_info_set_format(&prev_vf_info,
					     IA_CSS_FRAME_FORMAT_YUV_LINE);

		err = ia_css_pipe_get_preview_binarydesc(
		    pipe,
		    &preview_descr,
		    &prev_in_info,
		    &prev_bds_out_info,
		    &prev_out_info,
		    &prev_vf_info);
		if (err)
			return err;
		err = ia_css_binary_find(&preview_descr,
					 &mycs->preview_binary);
		if (err)
			return err;
	}

	if (need_vf_pp)
	{
		struct ia_css_binary_descr vf_pp_descr;

		/* Viewfinder post-processing */
		ia_css_pipe_get_vfpp_binarydesc(pipe, &vf_pp_descr,
						&mycs->preview_binary.out_frame_info[0],
						pipe_out_info);
		err = ia_css_binary_find(&vf_pp_descr,
					 &mycs->vf_pp_binary);
		if (err)
			return err;
	}

#ifdef USE_INPUT_SYSTEM_VERSION_2401
	/* When the input system is 2401, only the Direct Sensor Mode
	 * Offline Preview uses the ISP copy binary.
	 */
	need_isp_copy_binary = !online && sensor;
#else
	/* About pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY:
	 * This is typical the case with SkyCam (which has no input system) but it also applies to all cases
	 * where the driver chooses for memory based input frames. In these cases, a copy binary (which typical
	 * copies sensor data to DDR) does not have much use.
	 */
	if (!IS_ISP2401)
		need_isp_copy_binary = !online && !continuous;
	else
		need_isp_copy_binary = !online && !continuous && !(pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY);
#endif

	/* Copy */
	if (need_isp_copy_binary)
	{
		err = load_copy_binary(pipe,
				       &mycs->copy_binary,
				       &mycs->preview_binary);
		if (err)
			return err;
	}

	if (pipe->shading_table)
	{
		ia_css_shading_table_free(pipe->shading_table);
		pipe->shading_table = NULL;
	}

	return 0;
}

static void
ia_css_binary_unload(struct ia_css_binary *binary)
{
	ia_css_binary_destroy_isp_parameters(binary);
}

static int
unload_preview_binaries(struct ia_css_pipe *pipe) {
	IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);

	if ((!pipe) || (pipe->mode != IA_CSS_PIPE_ID_PREVIEW))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}
	ia_css_binary_unload(&pipe->pipe_settings.preview.copy_binary);
	ia_css_binary_unload(&pipe->pipe_settings.preview.preview_binary);
	ia_css_binary_unload(&pipe->pipe_settings.preview.vf_pp_binary);

	IA_CSS_LEAVE_ERR_PRIVATE(0);
	return 0;
}

static const struct ia_css_fw_info *last_output_firmware(
    const struct ia_css_fw_info *fw)
{
	const struct ia_css_fw_info *last_fw = NULL;
	/* fw can be NULL */
	IA_CSS_ENTER_LEAVE_PRIVATE("");

	for (; fw; fw = fw->next) {
		const struct ia_css_fw_info *info = fw;

		if (info->info.isp.sp.enable.output)
			last_fw = fw;
	}
	return last_fw;
}

static int add_firmwares(
    struct ia_css_pipeline *me,
    struct ia_css_binary *binary,
    const struct ia_css_fw_info *fw,
    const struct ia_css_fw_info *last_fw,
    unsigned int binary_mode,
    struct ia_css_frame *in_frame,
    struct ia_css_frame *out_frame,
    struct ia_css_frame *vf_frame,
    struct ia_css_pipeline_stage **my_stage,
    struct ia_css_pipeline_stage **vf_stage)
{
	int err = 0;
	struct ia_css_pipeline_stage *extra_stage = NULL;
	struct ia_css_pipeline_stage_desc stage_desc;

	/* all args can be NULL ??? */
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "add_firmwares() enter:\n");

	for (; fw; fw = fw->next) {
		struct ia_css_frame *out[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL};
		struct ia_css_frame *in = NULL;
		struct ia_css_frame *vf = NULL;

		if ((fw == last_fw) && (fw->info.isp.sp.enable.out_frame  != 0)) {
			out[0] = out_frame;
		}
		if (fw->info.isp.sp.enable.in_frame != 0) {
			in = in_frame;
		}
		if (fw->info.isp.sp.enable.out_frame != 0) {
			vf = vf_frame;
		}
		ia_css_pipe_get_firmwares_stage_desc(&stage_desc, binary,
						     out, in, vf, fw, binary_mode);
		err = ia_css_pipeline_create_and_add_stage(me,
			&stage_desc,
			&extra_stage);
		if (err)
			return err;
		if (fw->info.isp.sp.enable.output != 0)
			in_frame = extra_stage->args.out_frame[0];
		if (my_stage && !*my_stage && extra_stage)
			*my_stage = extra_stage;
		if (vf_stage && !*vf_stage && extra_stage &&
		    fw->info.isp.sp.enable.vf_veceven)
			*vf_stage = extra_stage;
	}
	return err;
}

static int add_vf_pp_stage(
    struct ia_css_pipe *pipe,
    struct ia_css_frame *in_frame,
    struct ia_css_frame *out_frame,
    struct ia_css_binary *vf_pp_binary,
    struct ia_css_pipeline_stage **vf_pp_stage)
{
	struct ia_css_pipeline *me = NULL;
	const struct ia_css_fw_info *last_fw = NULL;
	int err = 0;
	struct ia_css_frame *out_frames[IA_CSS_BINARY_MAX_OUTPUT_PORTS];
	struct ia_css_pipeline_stage_desc stage_desc;

	/* out_frame can be NULL ??? */

	if (!pipe)
		return -EINVAL;
	if (!in_frame)
		return -EINVAL;
	if (!vf_pp_binary)
		return -EINVAL;
	if (!vf_pp_stage)
		return -EINVAL;

	ia_css_pipe_util_create_output_frames(out_frames);
	me = &pipe->pipeline;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "add_vf_pp_stage() enter:\n");

	*vf_pp_stage = NULL;

	last_fw = last_output_firmware(pipe->vf_stage);
	if (!pipe->extra_config.disable_vf_pp) {
		if (last_fw) {
			ia_css_pipe_util_set_output_frames(out_frames, 0, NULL);
			ia_css_pipe_get_generic_stage_desc(&stage_desc, vf_pp_binary,
							   out_frames, in_frame, NULL);
		} else {
			ia_css_pipe_util_set_output_frames(out_frames, 0, out_frame);
			ia_css_pipe_get_generic_stage_desc(&stage_desc, vf_pp_binary,
							   out_frames, in_frame, NULL);
		}
		err = ia_css_pipeline_create_and_add_stage(me, &stage_desc, vf_pp_stage);
		if (err)
			return err;
		in_frame = (*vf_pp_stage)->args.out_frame[0];
	}
	err = add_firmwares(me, vf_pp_binary, pipe->vf_stage, last_fw,
			    IA_CSS_BINARY_MODE_VF_PP,
			    in_frame, out_frame, NULL,
			    vf_pp_stage, NULL);
	return err;
}

static int add_yuv_scaler_stage(
    struct ia_css_pipe *pipe,
    struct ia_css_pipeline *me,
    struct ia_css_frame *in_frame,
    struct ia_css_frame *out_frame,
    struct ia_css_frame *internal_out_frame,
    struct ia_css_binary *yuv_scaler_binary,
    struct ia_css_pipeline_stage **pre_vf_pp_stage)
{
	const struct ia_css_fw_info *last_fw;
	int err = 0;
	struct ia_css_frame *vf_frame = NULL;
	struct ia_css_frame *out_frames[IA_CSS_BINARY_MAX_OUTPUT_PORTS];
	struct ia_css_pipeline_stage_desc stage_desc;

	/* out_frame can be NULL ??? */
	assert(in_frame);
	assert(pipe);
	assert(me);
	assert(yuv_scaler_binary);
	assert(pre_vf_pp_stage);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "add_yuv_scaler_stage() enter:\n");

	*pre_vf_pp_stage = NULL;
	ia_css_pipe_util_create_output_frames(out_frames);

	last_fw = last_output_firmware(pipe->output_stage);

	if (last_fw) {
		ia_css_pipe_util_set_output_frames(out_frames, 0, NULL);
		ia_css_pipe_get_generic_stage_desc(&stage_desc,
						   yuv_scaler_binary, out_frames, in_frame, vf_frame);
	} else {
		ia_css_pipe_util_set_output_frames(out_frames, 0, out_frame);
		ia_css_pipe_util_set_output_frames(out_frames, 1, internal_out_frame);
		ia_css_pipe_get_generic_stage_desc(&stage_desc,
						   yuv_scaler_binary, out_frames, in_frame, vf_frame);
	}
	err = ia_css_pipeline_create_and_add_stage(me,
		&stage_desc,
		pre_vf_pp_stage);
	if (err)
		return err;
	in_frame = (*pre_vf_pp_stage)->args.out_frame[0];

	err = add_firmwares(me, yuv_scaler_binary, pipe->output_stage, last_fw,
			    IA_CSS_BINARY_MODE_CAPTURE_PP,
			    in_frame, out_frame, vf_frame,
			    NULL, pre_vf_pp_stage);
	/* If a firmware produce vf_pp output, we set that as vf_pp input */
	(*pre_vf_pp_stage)->args.vf_downscale_log2 =
	    yuv_scaler_binary->vf_downscale_log2;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "add_yuv_scaler_stage() leave:\n");
	return err;
}

static int add_capture_pp_stage(
    struct ia_css_pipe *pipe,
    struct ia_css_pipeline *me,
    struct ia_css_frame *in_frame,
    struct ia_css_frame *out_frame,
    struct ia_css_binary *capture_pp_binary,
    struct ia_css_pipeline_stage **capture_pp_stage)
{
	const struct ia_css_fw_info *last_fw = NULL;
	int err = 0;
	struct ia_css_frame *vf_frame = NULL;
	struct ia_css_frame *out_frames[IA_CSS_BINARY_MAX_OUTPUT_PORTS];
	struct ia_css_pipeline_stage_desc stage_desc;

	/* out_frame can be NULL ??? */
	assert(in_frame);
	assert(pipe);
	assert(me);
	assert(capture_pp_binary);
	assert(capture_pp_stage);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "add_capture_pp_stage() enter:\n");

	*capture_pp_stage = NULL;
	ia_css_pipe_util_create_output_frames(out_frames);

	last_fw = last_output_firmware(pipe->output_stage);
	err = ia_css_frame_allocate_from_info(&vf_frame,
					      &capture_pp_binary->vf_frame_info);
	if (err)
		return err;
	if (last_fw)	{
		ia_css_pipe_util_set_output_frames(out_frames, 0, NULL);
		ia_css_pipe_get_generic_stage_desc(&stage_desc,
						   capture_pp_binary, out_frames, NULL, vf_frame);
	} else {
		ia_css_pipe_util_set_output_frames(out_frames, 0, out_frame);
		ia_css_pipe_get_generic_stage_desc(&stage_desc,
						   capture_pp_binary, out_frames, NULL, vf_frame);
	}
	err = ia_css_pipeline_create_and_add_stage(me,
		&stage_desc,
		capture_pp_stage);
	if (err)
		return err;
	err = add_firmwares(me, capture_pp_binary, pipe->output_stage, last_fw,
			    IA_CSS_BINARY_MODE_CAPTURE_PP,
			    in_frame, out_frame, vf_frame,
			    NULL, capture_pp_stage);
	/* If a firmware produce vf_pp output, we set that as vf_pp input */
	if (*capture_pp_stage) {
		(*capture_pp_stage)->args.vf_downscale_log2 =
		    capture_pp_binary->vf_downscale_log2;
	}
	return err;
}

static void sh_css_setup_queues(void)
{
	const struct ia_css_fw_info *fw;
	unsigned int HIVE_ADDR_host_sp_queues_initialized;

	sh_css_hmm_buffer_record_init();

	sh_css_event_init_irq_mask();

	fw = &sh_css_sp_fw;
	HIVE_ADDR_host_sp_queues_initialized =
	    fw->info.sp.host_sp_queues_initialized;

	ia_css_bufq_init();

	/* set "host_sp_queues_initialized" to "true" */
	sp_dmem_store_uint32(SP0_ID,
			     (unsigned int)sp_address_of(host_sp_queues_initialized),
			     (uint32_t)(1));
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_setup_queues() leave:\n");
}

static int
init_vf_frameinfo_defaults(struct ia_css_pipe *pipe,
			   struct ia_css_frame *vf_frame, unsigned int idx) {
	int err = 0;
	unsigned int thread_id;
	enum sh_css_queue_id queue_id;

	assert(vf_frame);

	sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->info, idx);
	vf_frame->contiguous = false;
	vf_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
	ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
	ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME + idx, thread_id, &queue_id);
	vf_frame->dynamic_queue_id = queue_id;
	vf_frame->buf_type = IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME + idx;

	err = ia_css_frame_init_planes(vf_frame);
	return err;
}

#ifdef USE_INPUT_SYSTEM_VERSION_2401
static unsigned int
get_crop_lines_for_bayer_order(
    const struct ia_css_stream_config *config)
{
	assert(config);
	if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_BGGR)
	    || (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
		return 1;

	return 0;
}

static unsigned int
get_crop_columns_for_bayer_order(
    const struct ia_css_stream_config *config)
{
	assert(config);
	if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_RGGB)
	    || (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
		return 1;

	return 0;
}

/* This function is to get the sum of all extra pixels in addition to the effective
 * input, it includes dvs envelop and filter run-in */
static void get_pipe_extra_pixel(struct ia_css_pipe *pipe,
				 unsigned int *extra_row, unsigned int *extra_column)
{
	enum ia_css_pipe_id pipe_id = pipe->mode;
	unsigned int left_cropping = 0, top_cropping = 0;
	unsigned int i;
	struct ia_css_resolution dvs_env = pipe->config.dvs_envelope;

	/* The dvs envelope info may not be correctly sent down via pipe config
	 * The check is made and the correct value is populated in the binary info
	 * Use this value when computing crop, else excess lines may get trimmed
	 */
	switch (pipe_id) {
	case IA_CSS_PIPE_ID_PREVIEW:
		if (pipe->pipe_settings.preview.preview_binary.info) {
			left_cropping =
			    pipe->pipe_settings.preview.preview_binary.info->sp.pipeline.left_cropping;
			top_cropping =
			    pipe->pipe_settings.preview.preview_binary.info->sp.pipeline.top_cropping;
		}
		dvs_env = pipe->pipe_settings.preview.preview_binary.dvs_envelope;
		break;
	case IA_CSS_PIPE_ID_VIDEO:
		if (pipe->pipe_settings.video.video_binary.info) {
			left_cropping =
			    pipe->pipe_settings.video.video_binary.info->sp.pipeline.left_cropping;
			top_cropping =
			    pipe->pipe_settings.video.video_binary.info->sp.pipeline.top_cropping;
		}
		dvs_env = pipe->pipe_settings.video.video_binary.dvs_envelope;
		break;
	case IA_CSS_PIPE_ID_CAPTURE:
		for (i = 0; i < pipe->pipe_settings.capture.num_primary_stage; i++) {
			if (pipe->pipe_settings.capture.primary_binary[i].info) {
				left_cropping +=
				    pipe->pipe_settings.capture.primary_binary[i].info->sp.pipeline.left_cropping;
				top_cropping +=
				    pipe->pipe_settings.capture.primary_binary[i].info->sp.pipeline.top_cropping;
			}
			dvs_env.width +=
			    pipe->pipe_settings.capture.primary_binary[i].dvs_envelope.width;
			dvs_env.height +=
			    pipe->pipe_settings.capture.primary_binary[i].dvs_envelope.height;
		}
		break;
	default:
		break;
	}

	*extra_row = top_cropping + dvs_env.height;
	*extra_column = left_cropping + dvs_env.width;
}

void
ia_css_get_crop_offsets(
    struct ia_css_pipe *pipe,
    struct ia_css_frame_info *in_frame)
{
	unsigned int row = 0;
	unsigned int column = 0;
	struct ia_css_resolution *input_res;
	struct ia_css_resolution *effective_res;
	unsigned int extra_row = 0, extra_col = 0;
	unsigned int min_reqd_height, min_reqd_width;

	assert(pipe);
	assert(pipe->stream);
	assert(in_frame);

	IA_CSS_ENTER_PRIVATE("pipe = %p effective_wd = %u effective_ht = %u",
			     pipe, pipe->config.input_effective_res.width,
			     pipe->config.input_effective_res.height);

	input_res = &pipe->stream->config.input_config.input_res;
#ifndef ISP2401
	effective_res = &pipe->stream->config.input_config.effective_res;
#else
	effective_res = &pipe->config.input_effective_res;
#endif

	get_pipe_extra_pixel(pipe, &extra_row, &extra_col);

	in_frame->raw_bayer_order = pipe->stream->config.input_config.bayer_order;

	min_reqd_height = effective_res->height + extra_row;
	min_reqd_width = effective_res->width + extra_col;

	if (input_res->height > min_reqd_height) {
		row = (input_res->height - min_reqd_height) / 2;
		row &= ~0x1;
	}
	if (input_res->width > min_reqd_width) {
		column = (input_res->width - min_reqd_width) / 2;
		column &= ~0x1;
	}

	/*
	 * TODO:
	 * 1. Require the special support for RAW10 packed mode.
	 * 2. Require the special support for the online use cases.
	 */

	/* ISP expects GRBG bayer order, we skip one line and/or one row
	 * to correct in case the input bayer order is different.
	 */
	column += get_crop_columns_for_bayer_order(&pipe->stream->config);
	row += get_crop_lines_for_bayer_order(&pipe->stream->config);

	in_frame->crop_info.start_column = column;
	in_frame->crop_info.start_line = row;

	IA_CSS_LEAVE_PRIVATE("void start_col: %u start_row: %u", column, row);

	return;
}
#endif

static int
init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe,
				  struct ia_css_frame *frame, enum ia_css_frame_format format) {
	struct ia_css_frame *in_frame;
	int err = 0;
	unsigned int thread_id;
	enum sh_css_queue_id queue_id;

	assert(frame);
	in_frame = frame;

	in_frame->info.format = format;

#ifdef USE_INPUT_SYSTEM_VERSION_2401
	if (format == IA_CSS_FRAME_FORMAT_RAW)
		in_frame->info.format = (pipe->stream->config.pack_raw_pixels) ?
		IA_CSS_FRAME_FORMAT_RAW_PACKED : IA_CSS_FRAME_FORMAT_RAW;
#endif

	in_frame->info.res.width = pipe->stream->config.input_config.input_res.width;
	in_frame->info.res.height = pipe->stream->config.input_config.input_res.height;
	in_frame->info.raw_bit_depth =
	ia_css_pipe_util_pipe_input_format_bpp(pipe);
	ia_css_frame_info_set_width(&in_frame->info, pipe->stream->config.input_config.input_res.width, 0);
	in_frame->contiguous = false;
	in_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
	ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
	ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_INPUT_FRAME, thread_id, &queue_id);
	in_frame->dynamic_queue_id = queue_id;
	in_frame->buf_type = IA_CSS_BUFFER_TYPE_INPUT_FRAME;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	ia_css_get_crop_offsets(pipe, &in_frame->info);
#endif
	err = ia_css_frame_init_planes(in_frame);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "init_in_frameinfo_memory_defaults() bayer_order = %d:\n", in_frame->info.raw_bayer_order);

	return err;
}

static int
init_out_frameinfo_defaults(struct ia_css_pipe *pipe,
			    struct ia_css_frame *out_frame, unsigned int idx) {
	int err = 0;
	unsigned int thread_id;
	enum sh_css_queue_id queue_id;

	assert(out_frame);

	sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, idx);
	out_frame->contiguous = false;
	out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
	ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
	ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME + idx, thread_id, &queue_id);
	out_frame->dynamic_queue_id = queue_id;
	out_frame->buf_type = IA_CSS_BUFFER_TYPE_OUTPUT_FRAME + idx;
	err = ia_css_frame_init_planes(out_frame);

	return err;
}

/* Create stages for video pipe */
static int create_host_video_pipeline(struct ia_css_pipe *pipe)
{
	struct ia_css_pipeline_stage_desc stage_desc;
	struct ia_css_binary *copy_binary, *video_binary,
		       *yuv_scaler_binary, *vf_pp_binary;
	struct ia_css_pipeline_stage *copy_stage  = NULL;
	struct ia_css_pipeline_stage *video_stage = NULL;
	struct ia_css_pipeline_stage *yuv_scaler_stage  = NULL;
	struct ia_css_pipeline_stage *vf_pp_stage = NULL;
	struct ia_css_pipeline *me;
	struct ia_css_frame *in_frame = NULL;
	struct ia_css_frame *out_frame;
	struct ia_css_frame *out_frames[IA_CSS_BINARY_MAX_OUTPUT_PORTS];
	struct ia_css_frame *vf_frame = NULL;
	int err = 0;
	bool need_copy   = false;
	bool need_vf_pp  = false;
	bool need_yuv_pp = false;
	unsigned int num_output_pins;
	bool need_in_frameinfo_memory = false;

	unsigned int i, num_yuv_scaler;
	bool *is_output_stage = NULL;

	IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);
	if ((!pipe) || (!pipe->stream) || (pipe->mode != IA_CSS_PIPE_ID_VIDEO)) {
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}
	ia_css_pipe_util_create_output_frames(out_frames);
	out_frame = &pipe->out_frame_struct;

	/* pipeline already created as part of create_host_pipeline_structure */
	me = &pipe->pipeline;
	ia_css_pipeline_clean(me);

	me->dvs_frame_delay = pipe->dvs_frame_delay;

#ifdef USE_INPUT_SYSTEM_VERSION_2401
	/* When the input system is 2401, always enable 'in_frameinfo_memory'
	 * except for the following: online or continuous
	 */
	need_in_frameinfo_memory = !(pipe->stream->config.online ||
				     pipe->stream->config.continuous);
#else
	/* Construct in_frame info (only in case we have dynamic input */
	need_in_frameinfo_memory = pipe->stream->config.mode ==
				   IA_CSS_INPUT_MODE_MEMORY;
#endif

	/* Construct in_frame info (only in case we have dynamic input */
	if (need_in_frameinfo_memory) {
		in_frame = &pipe->in_frame_struct;
		err = init_in_frameinfo_memory_defaults(pipe, in_frame,
							IA_CSS_FRAME_FORMAT_RAW);
		if (err)
			goto ERR;
	}

	out_frame->data = 0;
	err = init_out_frameinfo_defaults(pipe, out_frame, 0);
	if (err)
		goto ERR;

	if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0]) {
		vf_frame = &pipe->vf_frame_struct;
		vf_frame->data = 0;
		err = init_vf_frameinfo_defaults(pipe, vf_frame, 0);
		if (err)
			goto ERR;
	}

	copy_binary  = &pipe->pipe_settings.video.copy_binary;
	video_binary = &pipe->pipe_settings.video.video_binary;
	vf_pp_binary = &pipe->pipe_settings.video.vf_pp_binary;
	num_output_pins = video_binary->info->num_output_pins;

	yuv_scaler_binary = pipe->pipe_settings.video.yuv_scaler_binary;
	num_yuv_scaler  = pipe->pipe_settings.video.num_yuv_scaler;
	is_output_stage = pipe->pipe_settings.video.is_output_stage;

	need_copy   = (copy_binary && copy_binary->info);
	need_vf_pp  = (vf_pp_binary && vf_pp_binary->info);
	need_yuv_pp = (yuv_scaler_binary && yuv_scaler_binary->info);

	if (need_copy) {
		ia_css_pipe_util_set_output_frames(out_frames, 0, NULL);
		ia_css_pipe_get_generic_stage_desc(&stage_desc, copy_binary,
						   out_frames, NULL, NULL);
		err = ia_css_pipeline_create_and_add_stage(me,
			&stage_desc,
			&copy_stage);
		if (err)
			goto ERR;
		in_frame = me->stages->args.out_frame[0];
	} else if (pipe->stream->config.continuous) {
#ifdef USE_INPUT_SYSTEM_VERSION_2401
		/* When continuous is enabled, configure in_frame with the
		 * last pipe, which is the copy pipe.
		 */
		in_frame = pipe->stream->last_pipe->continuous_frames[0];
#else
		in_frame = pipe->continuous_frames[0];
#endif
	}

	ia_css_pipe_util_set_output_frames(out_frames, 0,
					   need_yuv_pp ? NULL : out_frame);

	/* when the video binary supports a second output pin,
	   it can directly produce the vf_frame.  */
	if (need_vf_pp) {
		ia_css_pipe_get_generic_stage_desc(&stage_desc, video_binary,
						   out_frames, in_frame, NULL);
	} else {
		ia_css_pipe_get_generic_stage_desc(&stage_desc, video_binary,
						   out_frames, in_frame, vf_frame);
	}
	err = ia_css_pipeline_create_and_add_stage(me,
		&stage_desc,
		&video_stage);
	if (err)
		goto ERR;

	/* If we use copy iso video, the input must be yuv iso raw */
	if (video_stage) {
		video_stage->args.copy_vf =
		    video_binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_COPY;
		video_stage->args.copy_output = video_stage->args.copy_vf;
	}

	/* when the video binary supports only 1 output pin, vf_pp is needed to
	produce the vf_frame.*/
	if (need_vf_pp && video_stage) {
		in_frame = video_stage->args.out_vf_frame;
		err = add_vf_pp_stage(pipe, in_frame, vf_frame, vf_pp_binary,
				      &vf_pp_stage);
		if (err)
			goto ERR;
	}
	if (video_stage) {
		int frm;

		for (frm = 0; frm < NUM_TNR_FRAMES; frm++) {
			video_stage->args.tnr_frames[frm] =
			    pipe->pipe_settings.video.tnr_frames[frm];
		}
		for (frm = 0; frm < MAX_NUM_VIDEO_DELAY_FRAMES; frm++) {
			video_stage->args.delay_frames[frm] =
			    pipe->pipe_settings.video.delay_frames[frm];
		}
	}

	/* Append Extension on Video out, if enabled */
	if (!need_vf_pp && video_stage && pipe->config.acc_extension &&
	    (pipe->config.acc_extension->info.isp.type == IA_CSS_ACC_OUTPUT)) {
		struct ia_css_frame *out = NULL;
		struct ia_css_frame *in = NULL;

		if ((pipe->config.acc_extension->info.isp.sp.enable.output) &&
		    (pipe->config.acc_extension->info.isp.sp.enable.in_frame) &&
		    (pipe->config.acc_extension->info.isp.sp.enable.out_frame)) {
			/* In/Out Frame mapping to support output frame extension.*/
			out = video_stage->args.out_frame[0];
			err = ia_css_frame_allocate_from_info(&in, &pipe->output_info[0]);
			if (err)
				goto ERR;
			video_stage->args.out_frame[0] = in;
		}

		err = add_firmwares(me, video_binary, pipe->output_stage,
				    last_output_firmware(pipe->output_stage),
				    IA_CSS_BINARY_MODE_VIDEO,
				    in, out, NULL, &video_stage, NULL);
		if (err)
			goto ERR;
	}

	if (need_yuv_pp && video_stage) {
		struct ia_css_frame *tmp_in_frame = video_stage->args.out_frame[0];
		struct ia_css_frame *tmp_out_frame = NULL;

		for (i = 0; i < num_yuv_scaler; i++) {
			if (is_output_stage[i] == true) {
				tmp_out_frame = out_frame;
			} else {
				tmp_out_frame = NULL;
			}
			err = add_yuv_scaler_stage(pipe, me, tmp_in_frame, tmp_out_frame,
						   NULL,
						   &yuv_scaler_binary[i],
						   &yuv_scaler_stage);

			if (err) {
				IA_CSS_LEAVE_ERR_PRIVATE(err);
				return err;
			}
			/* we use output port 1 as internal output port */
			if (yuv_scaler_stage)
				tmp_in_frame = yuv_scaler_stage->args.out_frame[1];
		}
	}

	pipe->pipeline.acquire_isp_each_stage = false;
	ia_css_pipeline_finalize_stages(&pipe->pipeline,
					pipe->stream->config.continuous);

ERR:
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static int
create_host_acc_pipeline(struct ia_css_pipe *pipe) {
	int err = 0;
	const struct ia_css_fw_info *fw;
	unsigned int i;

	IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);
	if ((!pipe) || (!pipe->stream))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	pipe->pipeline.num_execs = pipe->config.acc_num_execs;
	/* Reset pipe_qos_config to default disable all QOS extension stages */
	if (pipe->config.acc_extension)
		pipe->pipeline.pipe_qos_config = 0;

	fw = pipe->vf_stage;
	for (i = 0; fw; fw = fw->next)
	{
		err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw);
		if (err)
			goto ERR;
	}

	for (i = 0; i < pipe->config.num_acc_stages; i++)
	{
		struct ia_css_fw_info *fw = pipe->config.acc_stages[i];

		err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw);
		if (err)
			goto ERR;
	}

	ia_css_pipeline_finalize_stages(&pipe->pipeline, pipe->stream->config.continuous);

ERR:
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/* Create stages for preview */
static int
create_host_preview_pipeline(struct ia_css_pipe *pipe) {
	struct ia_css_pipeline_stage *copy_stage = NULL;
	struct ia_css_pipeline_stage *preview_stage = NULL;
	struct ia_css_pipeline_stage *vf_pp_stage = NULL;
	struct ia_css_pipeline_stage_desc stage_desc;
	struct ia_css_pipeline *me = NULL;
	struct ia_css_binary *copy_binary, *preview_binary, *vf_pp_binary = NULL;
	struct ia_css_frame *in_frame = NULL;
	int err = 0;
	struct ia_css_frame *out_frame;
	struct ia_css_frame *out_frames[IA_CSS_BINARY_MAX_OUTPUT_PORTS];
	bool need_in_frameinfo_memory = false;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	bool sensor = false;
	bool buffered_sensor = false;
	bool online = false;
	bool continuous = false;
#endif

	IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);
	if ((!pipe) || (!pipe->stream) || (pipe->mode != IA_CSS_PIPE_ID_PREVIEW))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	ia_css_pipe_util_create_output_frames(out_frames);
	/* pipeline already created as part of create_host_pipeline_structure */
	me = &pipe->pipeline;
	ia_css_pipeline_clean(me);

#ifdef USE_INPUT_SYSTEM_VERSION_2401
	/* When the input system is 2401, always enable 'in_frameinfo_memory'
	 * except for the following:
	 * - Direct Sensor Mode Online Preview
	 * - Buffered Sensor Mode Online Preview
	 * - Direct Sensor Mode Continuous Preview
	 * - Buffered Sensor Mode Continuous Preview
	 */
	sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR);
	buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR);
	online = pipe->stream->config.online;
	continuous = pipe->stream->config.continuous;
	need_in_frameinfo_memory =
	!((sensor && (online || continuous)) || (buffered_sensor && (online || continuous)));
#else
	/* Construct in_frame info (only in case we have dynamic input */
	need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY;
#endif
	if (need_in_frameinfo_memory)
	{
		err = init_in_frameinfo_memory_defaults(pipe, &me->in_frame,
							IA_CSS_FRAME_FORMAT_RAW);
		if (err)
			goto ERR;

		in_frame = &me->in_frame;
	} else
	{
		in_frame = NULL;
	}

	err = init_out_frameinfo_defaults(pipe, &me->out_frame[0], 0);
	if (err)
		goto ERR;
	out_frame = &me->out_frame[0];

	copy_binary    = &pipe->pipe_settings.preview.copy_binary;
	preview_binary = &pipe->pipe_settings.preview.preview_binary;
	if (pipe->pipe_settings.preview.vf_pp_binary.info)
		vf_pp_binary = &pipe->pipe_settings.preview.vf_pp_binary;

	if (pipe->pipe_settings.preview.copy_binary.info)
	{
		ia_css_pipe_util_set_output_frames(out_frames, 0, NULL);
		ia_css_pipe_get_generic_stage_desc(&stage_desc, copy_binary,
						   out_frames, NULL, NULL);
		err = ia_css_pipeline_create_and_add_stage(me,
			&stage_desc,
			&copy_stage);
		if (err)
			goto ERR;
		in_frame = me->stages->args.out_frame[0];
#ifndef ISP2401
	} else
	{
#else
	} else if (pipe->stream->config.continuous)
	{
#endif
#ifdef USE_INPUT_SYSTEM_VERSION_2401
		/* When continuous is enabled, configure in_frame with the
		 * last pipe, which is the copy pipe.
		 */
		if (continuous || !online) {
			in_frame = pipe->stream->last_pipe->continuous_frames[0];
		}
#else
		in_frame = pipe->continuous_frames[0];
#endif
	}

	if (vf_pp_binary)
	{
		ia_css_pipe_util_set_output_frames(out_frames, 0, NULL);
		ia_css_pipe_get_generic_stage_desc(&stage_desc, preview_binary,
						   out_frames, in_frame, NULL);
	} else
	{
		ia_css_pipe_util_set_output_frames(out_frames, 0, out_frame);
		ia_css_pipe_get_generic_stage_desc(&stage_desc, preview_binary,
						   out_frames, in_frame, NULL);
	}
	err = ia_css_pipeline_create_and_add_stage(me,
		&stage_desc,
		&preview_stage);
	if (err)
		goto ERR;
	/* If we use copy iso preview, the input must be yuv iso raw */
	preview_stage->args.copy_vf =
	    preview_binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_COPY;
	preview_stage->args.copy_output = !preview_stage->args.copy_vf;
	if (preview_stage->args.copy_vf && !preview_stage->args.out_vf_frame)
	{
		/* in case of copy, use the vf frame as output frame */
		preview_stage->args.out_vf_frame =
		    preview_stage->args.out_frame[0];
	}
	if (vf_pp_binary)
	{
		if (preview_binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_COPY)
			in_frame = preview_stage->args.out_vf_frame;
		else
			in_frame = preview_stage->args.out_frame[0];
		err = add_vf_pp_stage(pipe, in_frame, out_frame, vf_pp_binary,
				      &vf_pp_stage);
		if (err)
			goto ERR;
	}

	pipe->pipeline.acquire_isp_each_stage = false;
	ia_css_pipeline_finalize_stages(&pipe->pipeline, pipe->stream->config.continuous);

ERR:
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static void send_raw_frames(struct ia_css_pipe *pipe)
{
	if (pipe->stream->config.continuous) {
		unsigned int i;

		sh_css_update_host2sp_cont_num_raw_frames
		(pipe->stream->config.init_num_cont_raw_buf, true);
		sh_css_update_host2sp_cont_num_raw_frames
		(pipe->stream->config.target_num_cont_raw_buf, false);

		/* Hand-over all the SP-internal buffers */
		for (i = 0; i < pipe->stream->config.init_num_cont_raw_buf; i++) {
			sh_css_update_host2sp_offline_frame(i,
							    pipe->continuous_frames[i], pipe->cont_md_buffers[i]);
		}
	}

	return;
}

static int
preview_start(struct ia_css_pipe *pipe) {
	struct ia_css_pipeline *me;
	struct ia_css_binary *copy_binary, *preview_binary, *vf_pp_binary = NULL;
	int err = 0;
	struct ia_css_pipe *copy_pipe, *capture_pipe;
	struct ia_css_pipe *acc_pipe;
	enum sh_css_pipe_config_override copy_ovrd;
	enum ia_css_input_mode preview_pipe_input_mode;
	const struct ia_css_coordinate *coord = NULL;
	const struct ia_css_isp_parameters *params = NULL;

	IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);
	if ((!pipe) || (!pipe->stream) || (pipe->mode != IA_CSS_PIPE_ID_PREVIEW))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	me = &pipe->pipeline;

	preview_pipe_input_mode = pipe->stream->config.mode;

	copy_pipe    = pipe->pipe_settings.preview.copy_pipe;
	capture_pipe = pipe->pipe_settings.preview.capture_pipe;
	acc_pipe     = pipe->pipe_settings.preview.acc_pipe;

	copy_binary    = &pipe->pipe_settings.preview.copy_binary;
	preview_binary = &pipe->pipe_settings.preview.preview_binary;
	if (pipe->pipe_settings.preview.vf_pp_binary.info)
		vf_pp_binary = &pipe->pipe_settings.preview.vf_pp_binary;

	sh_css_metrics_start_frame();

#if defined(USE_INPUT_SYSTEM_VERSION_2) || defined(USE_INPUT_SYSTEM_VERSION_2401)
	/* multi stream video needs mipi buffers */
	err = send_mipi_frames(pipe);
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}
#endif
	send_raw_frames(pipe);

	{
		unsigned int thread_id;

		ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
		copy_ovrd = 1 << thread_id;

		if (pipe->stream->cont_capt)
		{
			ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(capture_pipe),
							 &thread_id);
			copy_ovrd |= 1 << thread_id;
		}
	}

	if (IS_ISP2401) {
		coord = &pipe->config.internal_frame_origin_bqs_on_sctbl;
		params = pipe->stream->isp_params_configs;
	}

	/* Construct and load the copy pipe */
	if (pipe->stream->config.continuous)
	{
		sh_css_sp_init_pipeline(&copy_pipe->pipeline,
					IA_CSS_PIPE_ID_COPY,
					(uint8_t)ia_css_pipe_get_pipe_num(copy_pipe),
					false,
					pipe->stream->config.pixels_per_clock == 2, false,
					false, pipe->required_bds_factor,
					copy_ovrd,
					pipe->stream->config.mode,
					&pipe->stream->config.metadata_config,
					&pipe->stream->info.metadata_info,
#if !defined(HAS_NO_INPUT_SYSTEM)
					pipe->stream->config.source.port.port,
#endif
					coord,
					params);

		/* make the preview pipe start with mem mode input, copy handles
		   the actual mode */
		preview_pipe_input_mode = IA_CSS_INPUT_MODE_MEMORY;
	}

	/* Construct and load the capture pipe */
	if (pipe->stream->cont_capt)
	{
		sh_css_sp_init_pipeline(&capture_pipe->pipeline,
					IA_CSS_PIPE_ID_CAPTURE,
					(uint8_t)ia_css_pipe_get_pipe_num(capture_pipe),
					capture_pipe->config.default_capture_config.enable_xnr != 0,
					capture_pipe->stream->config.pixels_per_clock == 2,
					true, /* continuous */
					false, /* offline */
					capture_pipe->required_bds_factor,
					0,
					IA_CSS_INPUT_MODE_MEMORY,
					&pipe->stream->config.metadata_config,
					&pipe->stream->info.metadata_info,
#if !defined(HAS_NO_INPUT_SYSTEM)
					(enum mipi_port_id)0,
#endif
					coord,
					params);
	}

	if (acc_pipe)
	{
		sh_css_sp_init_pipeline(&acc_pipe->pipeline,
					IA_CSS_PIPE_ID_ACC,
					(uint8_t)ia_css_pipe_get_pipe_num(acc_pipe),
					false,
					pipe->stream->config.pixels_per_clock == 2,
					false, /* continuous */
					false, /* offline */
					pipe->required_bds_factor,
					0,
					IA_CSS_INPUT_MODE_MEMORY,
					NULL,
					NULL,
#if !defined(HAS_NO_INPUT_SYSTEM)
					(enum mipi_port_id)0,
#endif
					coord,
					params);
	}

	start_pipe(pipe, copy_ovrd, preview_pipe_input_mode);

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

int
ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe,
			   const struct ia_css_buffer *buffer) {
	int return_err = 0;
	unsigned int thread_id;
	enum sh_css_queue_id queue_id;
	struct ia_css_pipeline *pipeline;
	struct ia_css_pipeline_stage *stage;
	struct ia_css_rmgr_vbuf_handle p_vbuf;
	struct ia_css_rmgr_vbuf_handle *h_vbuf;
	struct sh_css_hmm_buffer ddr_buffer;
	enum ia_css_buffer_type buf_type;
	enum ia_css_pipe_id pipe_id;
	bool ret_err;

	IA_CSS_ENTER("pipe=%p, buffer=%p", pipe, buffer);

	if ((!pipe) || (!buffer))
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	buf_type = buffer->type;
	/* following code will be enabled when IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME
	   is removed */
#if 0
	if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
	{
		bool found_pipe = false;

		for (i = 0; i < IA_CSS_PIPE_MAX_OUTPUT_STAGE; i++) {
			if ((buffer->data.frame->info.res.width == pipe->output_info[i].res.width) &&
			    (buffer->data.frame->info.res.height == pipe->output_info[i].res.height)) {
				buf_type += i;
				found_pipe = true;
				break;
			}
		}
		if (!found_pipe)
			return -EINVAL;
	}
	if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME)
	{
		bool found_pipe = false;

		for (i = 0; i < IA_CSS_PIPE_MAX_OUTPUT_STAGE; i++) {
			if ((buffer->data.frame->info.res.width == pipe->vf_output_info[i].res.width) &&
			    (buffer->data.frame->info.res.height == pipe->vf_output_info[i].res.height)) {
				buf_type += i;
				found_pipe = true;
				break;
			}
		}
		if (!found_pipe)
			return -EINVAL;
	}
#endif
	pipe_id = pipe->mode;

	IA_CSS_LOG("pipe_id=%d, buf_type=%d", pipe_id, buf_type);

	assert(pipe_id < IA_CSS_PIPE_ID_NUM);
	assert(buf_type < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE);
	if ((buf_type == IA_CSS_BUFFER_TYPE_INVALID) ||
	    (buf_type >= IA_CSS_NUM_DYNAMIC_BUFFER_TYPE) ||
	    (pipe_id >= IA_CSS_PIPE_ID_NUM))
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	ret_err = ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
	if (!ret_err)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	ret_err = ia_css_query_internal_queue_id(buf_type, thread_id, &queue_id);
	if (!ret_err)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	if ((queue_id <= SH_CSS_INVALID_QUEUE_ID) || (queue_id >= SH_CSS_MAX_NUM_QUEUES))
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	if (!sh_css_sp_is_running())
	{
		IA_CSS_LOG("SP is not running!");
		IA_CSS_LEAVE_ERR(-EBUSY);
		/* SP is not running. The queues are not valid */
		return -EBUSY;
	}

	pipeline = &pipe->pipeline;

	assert(pipeline ||
	       pipe_id == IA_CSS_PIPE_ID_COPY ||
	       pipe_id == IA_CSS_PIPE_ID_ACC);

	assert(sizeof(NULL) <= sizeof(ddr_buffer.kernel_ptr));
	ddr_buffer.kernel_ptr = HOST_ADDRESS(NULL);
	ddr_buffer.cookie_ptr = buffer->driver_cookie;
	ddr_buffer.timing_data = buffer->timing_data;

	if (buf_type == IA_CSS_BUFFER_TYPE_3A_STATISTICS)
	{
		if (!buffer->data.stats_3a) {
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}
		ddr_buffer.kernel_ptr = HOST_ADDRESS(buffer->data.stats_3a);
		ddr_buffer.payload.s3a = *buffer->data.stats_3a;
	} else if (buf_type == IA_CSS_BUFFER_TYPE_DIS_STATISTICS)
	{
		if (!buffer->data.stats_dvs) {
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}
		ddr_buffer.kernel_ptr = HOST_ADDRESS(buffer->data.stats_dvs);
		ddr_buffer.payload.dis = *buffer->data.stats_dvs;
	} else if (buf_type == IA_CSS_BUFFER_TYPE_METADATA)
	{
		if (!buffer->data.metadata) {
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}
		ddr_buffer.kernel_ptr = HOST_ADDRESS(buffer->data.metadata);
		ddr_buffer.payload.metadata = *buffer->data.metadata;
	} else if ((buf_type == IA_CSS_BUFFER_TYPE_INPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME))
	{
		if (!buffer->data.frame) {
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}
		ddr_buffer.kernel_ptr = HOST_ADDRESS(buffer->data.frame);
		ddr_buffer.payload.frame.frame_data = buffer->data.frame->data;
		ddr_buffer.payload.frame.flashed = 0;

		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
				    "ia_css_pipe_enqueue_buffer() buf_type=%d, data(DDR address)=0x%x\n",
				    buf_type, buffer->data.frame->data);

#if CONFIG_ON_FRAME_ENQUEUE()
		return_err = set_config_on_frame_enqueue(
				 &buffer->data.frame->info,
				 &ddr_buffer.payload.frame);
		if (return_err) {
			IA_CSS_LEAVE_ERR(return_err);
			return return_err;
		}
#endif
	}

	/* start of test for using rmgr for acq/rel memory */
	p_vbuf.vptr = 0;
	p_vbuf.count = 0;
	p_vbuf.size = sizeof(struct sh_css_hmm_buffer);
	h_vbuf = &p_vbuf;
	/* TODO: change next to correct pool for optimization */
	ia_css_rmgr_acq_vbuf(hmm_buffer_pool, &h_vbuf);

	assert(h_vbuf);
	assert(h_vbuf->vptr != 0x0);

	if ((!h_vbuf) || (h_vbuf->vptr == 0x0))
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	hmm_store(h_vbuf->vptr,
		   (void *)(&ddr_buffer),
		   sizeof(struct sh_css_hmm_buffer));
	if ((buf_type == IA_CSS_BUFFER_TYPE_3A_STATISTICS)
	    || (buf_type == IA_CSS_BUFFER_TYPE_DIS_STATISTICS)
	    || (buf_type == IA_CSS_BUFFER_TYPE_LACE_STATISTICS))
	{
		if (!pipeline) {
			ia_css_rmgr_rel_vbuf(hmm_buffer_pool, &h_vbuf);
			IA_CSS_LOG("pipeline is empty!");
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}

		for (stage = pipeline->stages; stage; stage = stage->next) {
			/* The SP will read the params
				after it got empty 3a and dis */
			if (STATS_ENABLED(stage)) {
				/* there is a stage that needs it */
				return_err = ia_css_bufq_enqueue_buffer(thread_id,
									queue_id,
									(uint32_t)h_vbuf->vptr);
			}
		}
	} else if ((buf_type == IA_CSS_BUFFER_TYPE_INPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME)
		   || (buf_type == IA_CSS_BUFFER_TYPE_METADATA))
	{
		return_err = ia_css_bufq_enqueue_buffer(thread_id,
							queue_id,
							(uint32_t)h_vbuf->vptr);
#if defined(SH_CSS_ENABLE_PER_FRAME_PARAMS)
		if (!(return_err) &&
		    (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)) {
			IA_CSS_LOG("pfp: enqueued OF %d to q %d thread %d",
				   ddr_buffer.payload.frame.frame_data,
				   queue_id, thread_id);
		}
#endif
	}

	if (!return_err)
	{
		if (sh_css_hmm_buffer_record_acquire(
			h_vbuf, buf_type,
			HOST_ADDRESS(ddr_buffer.kernel_ptr))) {
			IA_CSS_LOG("send vbuf=%p", h_vbuf);
		} else {
			return_err = -EINVAL;
			IA_CSS_ERROR("hmm_buffer_record[]: no available slots\n");
		}
	}

	/*
	 * Tell the SP which queues are not empty,
	 * by sending the software event.
	 */
	if (!return_err)
	{
		if (!sh_css_sp_is_running()) {
			/* SP is not running. The queues are not valid */
			IA_CSS_LOG("SP is not running!");
			IA_CSS_LEAVE_ERR(-EBUSY);
			return -EBUSY;
		}
		return_err = ia_css_bufq_enqueue_psys_event(
				 IA_CSS_PSYS_SW_EVENT_BUFFER_ENQUEUED,
				 (uint8_t)thread_id,
				 queue_id,
				 0);
	} else
	{
		ia_css_rmgr_rel_vbuf(hmm_buffer_pool, &h_vbuf);
		IA_CSS_ERROR("buffer not enqueued");
	}

	IA_CSS_LEAVE("return value = %d", return_err);

	return return_err;
}

/*
 * TODO: Free up the hmm memory space.
	 */
int
ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
			   struct ia_css_buffer *buffer) {
	int return_err;
	enum sh_css_queue_id queue_id;
	ia_css_ptr ddr_buffer_addr = (ia_css_ptr)0;
	struct sh_css_hmm_buffer ddr_buffer;
	enum ia_css_buffer_type buf_type;
	enum ia_css_pipe_id pipe_id;
	unsigned int thread_id;
	hrt_address kernel_ptr = 0;
	bool ret_err;

	IA_CSS_ENTER("pipe=%p, buffer=%p", pipe, buffer);

	if ((!pipe) || (!buffer))
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	pipe_id = pipe->mode;

	buf_type = buffer->type;

	IA_CSS_LOG("pipe_id=%d, buf_type=%d", pipe_id, buf_type);

	ddr_buffer.kernel_ptr = 0;

	ret_err = ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
	if (!ret_err)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	ret_err = ia_css_query_internal_queue_id(buf_type, thread_id, &queue_id);
	if (!ret_err)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	if ((queue_id <= SH_CSS_INVALID_QUEUE_ID) || (queue_id >= SH_CSS_MAX_NUM_QUEUES))
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	if (!sh_css_sp_is_running())
	{
		IA_CSS_LOG("SP is not running!");
		IA_CSS_LEAVE_ERR(-EBUSY);
		/* SP is not running. The queues are not valid */
		return -EBUSY;
	}

	return_err = ia_css_bufq_dequeue_buffer(queue_id,
						(uint32_t *)&ddr_buffer_addr);

	if (!return_err)
	{
		struct ia_css_frame *frame;
		struct sh_css_hmm_buffer_record *hmm_buffer_record = NULL;

		IA_CSS_LOG("receive vbuf=%x", (int)ddr_buffer_addr);

		/* Validate the ddr_buffer_addr and buf_type */
		hmm_buffer_record = sh_css_hmm_buffer_record_validate(
		    ddr_buffer_addr, buf_type);
		if (hmm_buffer_record) {
			/* valid hmm_buffer_record found. Save the kernel_ptr
			 * for validation after performing hmm_load.  The
			 * vbuf handle and buffer_record can be released.
			 */
			kernel_ptr = hmm_buffer_record->kernel_ptr;
			ia_css_rmgr_rel_vbuf(hmm_buffer_pool, &hmm_buffer_record->h_vbuf);
			sh_css_hmm_buffer_record_reset(hmm_buffer_record);
		} else {
			IA_CSS_ERROR("hmm_buffer_record not found (0x%x) buf_type(%d)",
				     ddr_buffer_addr, buf_type);
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}

		hmm_load(ddr_buffer_addr,
			  &ddr_buffer,
			  sizeof(struct sh_css_hmm_buffer));

		/* if the kernel_ptr is 0 or an invalid, return an error.
		 * do not access the buffer via the kernal_ptr.
		 */
		if ((ddr_buffer.kernel_ptr == 0) ||
		    (kernel_ptr != HOST_ADDRESS(ddr_buffer.kernel_ptr))) {
			IA_CSS_ERROR("kernel_ptr invalid");
			IA_CSS_ERROR("expected: (0x%llx)", (u64)kernel_ptr);
			IA_CSS_ERROR("actual: (0x%llx)", (u64)HOST_ADDRESS(ddr_buffer.kernel_ptr));
			IA_CSS_ERROR("buf_type: %d\n", buf_type);
			IA_CSS_LEAVE_ERR(-EINVAL);
			return -EINVAL;
		}

		if (ddr_buffer.kernel_ptr != 0) {
			/* buffer->exp_id : all instances to be removed later once the driver change
			 * is completed. See patch #5758 for reference */
			buffer->exp_id = 0;
			buffer->driver_cookie = ddr_buffer.cookie_ptr;
			buffer->timing_data = ddr_buffer.timing_data;

			if ((buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) ||
			    (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME)) {
				buffer->isys_eof_clock_tick.ticks = ddr_buffer.isys_eof_clock_tick;
			}

			switch (buf_type) {
			case IA_CSS_BUFFER_TYPE_INPUT_FRAME:
			case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME:
			case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME:
				if ((pipe) && (pipe->stop_requested == true)) {
#if defined(USE_INPUT_SYSTEM_VERSION_2)
					/* free mipi frames only for old input system
					 * for 2401 it is done in ia_css_stream_destroy call
					 */
					return_err = free_mipi_frames(pipe);
					if (return_err) {
						IA_CSS_LOG("free_mipi_frames() failed");
						IA_CSS_LEAVE_ERR(return_err);
						return return_err;
					}
#endif
					pipe->stop_requested = false;
				}
				fallthrough;
			case IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME:
			case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME:
				frame = (struct ia_css_frame *)HOST_ADDRESS(ddr_buffer.kernel_ptr);
				buffer->data.frame = frame;
				buffer->exp_id = ddr_buffer.payload.frame.exp_id;
				frame->exp_id = ddr_buffer.payload.frame.exp_id;
				frame->isp_config_id = ddr_buffer.payload.frame.isp_parameters_id;
				if (ddr_buffer.payload.frame.flashed == 1)
					frame->flash_state =
					    IA_CSS_FRAME_FLASH_STATE_PARTIAL;
				if (ddr_buffer.payload.frame.flashed == 2)
					frame->flash_state =
					    IA_CSS_FRAME_FLASH_STATE_FULL;
				frame->valid = pipe->num_invalid_frames == 0;
				if (!frame->valid)
					pipe->num_invalid_frames--;

				if (frame->info.format == IA_CSS_FRAME_FORMAT_BINARY_8) {
#ifdef USE_INPUT_SYSTEM_VERSION_2401
					frame->planes.binary.size = frame->data_bytes;
#else
					frame->planes.binary.size =
					    sh_css_sp_get_binary_copy_size();
#endif
				}
#if defined(SH_CSS_ENABLE_PER_FRAME_PARAMS)
				if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) {
					IA_CSS_LOG("pfp: dequeued OF %d with config id %d thread %d",
						   frame->data, frame->isp_config_id, thread_id);
				}
#endif

				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
						    "ia_css_pipe_dequeue_buffer() buf_type=%d, data(DDR address)=0x%x\n",
						    buf_type, buffer->data.frame->data);

				break;
			case IA_CSS_BUFFER_TYPE_3A_STATISTICS:
				buffer->data.stats_3a =
				    (struct ia_css_isp_3a_statistics *)HOST_ADDRESS(ddr_buffer.kernel_ptr);
				buffer->exp_id = ddr_buffer.payload.s3a.exp_id;
				buffer->data.stats_3a->exp_id = ddr_buffer.payload.s3a.exp_id;
				buffer->data.stats_3a->isp_config_id = ddr_buffer.payload.s3a.isp_config_id;
				break;
			case IA_CSS_BUFFER_TYPE_DIS_STATISTICS:
				buffer->data.stats_dvs =
				    (struct ia_css_isp_dvs_statistics *)
				    HOST_ADDRESS(ddr_buffer.kernel_ptr);
				buffer->exp_id = ddr_buffer.payload.dis.exp_id;
				buffer->data.stats_dvs->exp_id = ddr_buffer.payload.dis.exp_id;
				break;
			case IA_CSS_BUFFER_TYPE_LACE_STATISTICS:
				break;
			case IA_CSS_BUFFER_TYPE_METADATA:
				buffer->data.metadata =
				    (struct ia_css_metadata *)HOST_ADDRESS(ddr_buffer.kernel_ptr);
				buffer->exp_id = ddr_buffer.payload.metadata.exp_id;
				buffer->data.metadata->exp_id = ddr_buffer.payload.metadata.exp_id;
				break;
			default:
				return_err = -EINVAL;
				break;
			}
		}
	}

	/*
	 * Tell the SP which queues are not full,
	 * by sending the software event.
	 */
	if (!return_err)
	{
		if (!sh_css_sp_is_running()) {
			IA_CSS_LOG("SP is not running!");
			IA_CSS_LEAVE_ERR(-EBUSY);
			/* SP is not running. The queues are not valid */
			return -EBUSY;
		}
		ia_css_bufq_enqueue_psys_event(
		    IA_CSS_PSYS_SW_EVENT_BUFFER_DEQUEUED,
		    0,
		    queue_id,
		    0);
	}
	IA_CSS_LEAVE("buffer=%p", buffer);

	return return_err;
}

/*
 * Cannot Move this to event module as it is of ia_css_event_type which is declared in ia_css.h
 * TODO: modify and move it if possible.
 *
 * !!!IMPORTANT!!! KEEP THE FOLLOWING IN SYNC:
 * 1) "enum ia_css_event_type"					(ia_css_event_public.h)
 * 2) "enum sh_css_sp_event_type"				(sh_css_internal.h)
 * 3) "enum ia_css_event_type event_id_2_event_mask"		(event_handler.sp.c)
 * 4) "enum ia_css_event_type convert_event_sp_to_host_domain"	(sh_css.c)
 */
static enum ia_css_event_type convert_event_sp_to_host_domain[] = {
	IA_CSS_EVENT_TYPE_OUTPUT_FRAME_DONE,	/** Output frame ready. */
	IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE,	/** Second output frame ready. */
	IA_CSS_EVENT_TYPE_VF_OUTPUT_FRAME_DONE,	/** Viewfinder Output frame ready. */
	IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE,	/** Second viewfinder Output frame ready. */
	IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE,	/** Indication that 3A statistics are available. */
	IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE,	/** Indication that DIS statistics are available. */
	IA_CSS_EVENT_TYPE_PIPELINE_DONE,	/** Pipeline Done event, sent after last pipeline stage. */
	IA_CSS_EVENT_TYPE_FRAME_TAGGED,		/** Frame tagged. */
	IA_CSS_EVENT_TYPE_INPUT_FRAME_DONE,	/** Input frame ready. */
	IA_CSS_EVENT_TYPE_METADATA_DONE,	/** Metadata ready. */
	IA_CSS_EVENT_TYPE_LACE_STATISTICS_DONE,	/** Indication that LACE statistics are available. */
	IA_CSS_EVENT_TYPE_ACC_STAGE_COMPLETE,	/** Extension stage executed. */
	IA_CSS_EVENT_TYPE_TIMER,		/** Timing measurement data. */
	IA_CSS_EVENT_TYPE_PORT_EOF,		/** End Of Frame event, sent when in buffered sensor mode. */
	IA_CSS_EVENT_TYPE_FW_WARNING,		/** Performance warning encountered by FW */
	IA_CSS_EVENT_TYPE_FW_ASSERT,		/** Assertion hit by FW */
	0,					/* error if sp passes  SH_CSS_SP_EVENT_NR_OF_TYPES as a valid event. */
};

int
ia_css_dequeue_event(struct ia_css_event *event) {
	return ia_css_dequeue_psys_event(event);
}

int
ia_css_dequeue_psys_event(struct ia_css_event *event) {
	enum ia_css_pipe_id pipe_id = 0;
	u8 payload[4] = {0, 0, 0, 0};
	int ret_err;

	/*TODO:
	 * a) use generic decoding function , same as the one used by sp.
	 * b) group decode and dequeue into eventQueue module
	 *
	 * We skip the IA_CSS_ENTER logging call
	 * to avoid flooding the logs when the host application
	 * uses polling. */
	if (!event)
		return -EINVAL;

	if (!sh_css_sp_is_running())
	{
		/* SP is not running. The queues are not valid */
		return -EBUSY;
	}

	/* dequeue the event (if any) from the psys event queue */
	ret_err = ia_css_bufq_dequeue_psys_event(payload);
	if (ret_err)
		return ret_err;

	IA_CSS_LOG("event dequeued from psys event queue");

	/* Tell the SP that we dequeued an event from the event queue. */
	ia_css_bufq_enqueue_psys_event(
	    IA_CSS_PSYS_SW_EVENT_EVENT_DEQUEUED, 0, 0, 0);

	/* Events are decoded into 4 bytes of payload, the first byte
	 * contains the sp event type. This is converted to a host enum.
	 * TODO: can this enum conversion be eliminated */
	event->type = convert_event_sp_to_host_domain[payload[0]];
	/* Some sane default values since not all events use all fields. */
	event->pipe = NULL;
	event->port = MIPI_PORT0_ID;
	event->exp_id = 0;
	event->fw_warning = IA_CSS_FW_WARNING_NONE;
	event->fw_handle = 0;
	event->timer_data = 0;
	event->timer_code = 0;
	event->timer_subcode = 0;

	if (event->type == IA_CSS_EVENT_TYPE_TIMER)
	{
		/* timer event ??? get the 2nd event and decode the data into the event struct */
		u32 tmp_data;
		/* 1st event: LSB 16-bit timer data and code */
		event->timer_data = ((payload[1] & 0xFF) | ((payload[3] & 0xFF) << 8));
		event->timer_code = payload[2];
		payload[0] = payload[1] = payload[2] = payload[3] = 0;
		ret_err = ia_css_bufq_dequeue_psys_event(payload);
		if (ret_err) {
			/* no 2nd event ??? an error */
			/* Putting IA_CSS_ERROR is resulting in failures in
			 * Merrifield smoke testing  */
			IA_CSS_WARNING("Timer: Error de-queuing the 2nd TIMER event!!!\n");
			return ret_err;
		}
		ia_css_bufq_enqueue_psys_event(
		    IA_CSS_PSYS_SW_EVENT_EVENT_DEQUEUED, 0, 0, 0);
		event->type = convert_event_sp_to_host_domain[payload[0]];
		/* It's a timer */
		if (event->type == IA_CSS_EVENT_TYPE_TIMER) {
			/* 2nd event data: MSB 16-bit timer and subcode */
			tmp_data = ((payload[1] & 0xFF) | ((payload[3] & 0xFF) << 8));
			event->timer_data |= (tmp_data << 16);
			event->timer_subcode = payload[2];
		}
		/* It's a non timer event. So clear first half of the timer event data.
		* If the second part of the TIMER event is not received, we discard
		* the first half of the timer data and process the non timer event without
		* affecting the flow. So the non timer event falls through
		* the code. */
		else {
			event->timer_data = 0;
			event->timer_code = 0;
			event->timer_subcode = 0;
			IA_CSS_ERROR("Missing 2nd timer event. Timer event discarded");
		}
	}
	if (event->type == IA_CSS_EVENT_TYPE_PORT_EOF)
	{
		event->port = (enum mipi_port_id)payload[1];
		event->exp_id = payload[3];
	} else if (event->type == IA_CSS_EVENT_TYPE_FW_WARNING)
	{
		event->fw_warning = (enum ia_css_fw_warning)payload[1];
		/* exp_id is only available in these warning types */
		if (event->fw_warning == IA_CSS_FW_WARNING_EXP_ID_LOCKED ||
		    event->fw_warning == IA_CSS_FW_WARNING_TAG_EXP_ID_FAILED)
			event->exp_id = payload[3];
	} else if (event->type == IA_CSS_EVENT_TYPE_FW_ASSERT)
	{
		event->fw_assert_module_id = payload[1]; /* module */
		event->fw_assert_line_no = (payload[2] << 8) + payload[3];
		/* payload[2] is line_no>>8, payload[3] is line_no&0xff */
	} else if (event->type != IA_CSS_EVENT_TYPE_TIMER)
	{
		/* pipe related events.
		 * payload[1] contains the pipe_num,
		 * payload[2] contains the pipe_id. These are different. */
		event->pipe = find_pipe_by_num(payload[1]);
		pipe_id = (enum ia_css_pipe_id)payload[2];
		/* Check to see if pipe still exists */
		if (!event->pipe)
			return -EBUSY;

		if (event->type == IA_CSS_EVENT_TYPE_FRAME_TAGGED) {
			/* find the capture pipe that goes with this */
			int i, n;

			n = event->pipe->stream->num_pipes;
			for (i = 0; i < n; i++) {
				struct ia_css_pipe *p =
					    event->pipe->stream->pipes[i];
				if (p->config.mode == IA_CSS_PIPE_MODE_CAPTURE) {
					event->pipe = p;
					break;
				}
			}
			event->exp_id = payload[3];
		}
		if (event->type == IA_CSS_EVENT_TYPE_ACC_STAGE_COMPLETE) {
			/* payload[3] contains the acc fw handle. */
			u32 stage_num = (uint32_t)payload[3];

			ret_err = ia_css_pipeline_get_fw_from_stage(
				      &event->pipe->pipeline,
				      stage_num,
				      &event->fw_handle);
			if (ret_err) {
				IA_CSS_ERROR("Invalid stage num received for ACC event. stage_num:%u",
					     stage_num);
				return ret_err;
			}
		}
	}

	if (event->pipe)
		IA_CSS_LEAVE("event_id=%d, pipe_id=%d", event->type, pipe_id);
	else
		IA_CSS_LEAVE("event_id=%d", event->type);

	return 0;
}

int
ia_css_dequeue_isys_event(struct ia_css_event *event) {
	u8 payload[4] = {0, 0, 0, 0};
	int err = 0;

	/* We skip the IA_CSS_ENTER logging call
	 * to avoid flooding the logs when the host application
	 * uses polling. */
	if (!event)
		return -EINVAL;

	if (!sh_css_sp_is_running())
	{
		/* SP is not running. The queues are not valid */
		return -EBUSY;
	}

	err = ia_css_bufq_dequeue_isys_event(payload);
	if (err)
		return err;

	IA_CSS_LOG("event dequeued from isys event queue");

	/* Update SP state to indicate that element was dequeued. */
	ia_css_bufq_enqueue_isys_event(IA_CSS_ISYS_SW_EVENT_EVENT_DEQUEUED);

	/* Fill return struct with appropriate info */
	event->type = IA_CSS_EVENT_TYPE_PORT_EOF;
	/* EOF events are associated with a CSI port, not with a pipe */
	event->pipe = NULL;
	event->port = payload[1];
	event->exp_id = payload[3];

	IA_CSS_LEAVE_ERR(err);
	return err;
}

static void
acc_start(struct ia_css_pipe *pipe)
{
	assert(pipe);
	assert(pipe->stream);

	start_pipe(pipe, SH_CSS_PIPE_CONFIG_OVRD_NO_OVRD,
		   pipe->stream->config.mode);
}

static int
sh_css_pipe_start(struct ia_css_stream *stream) {
	int err = 0;

	struct ia_css_pipe *pipe;
	enum ia_css_pipe_id pipe_id;
	unsigned int thread_id;

	IA_CSS_ENTER_PRIVATE("stream = %p", stream);

	if (!stream)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}
	pipe = stream->last_pipe;
	if (!pipe)
	{
		IA_CSS_LEAVE_ERR(-EINVAL);
		return -EINVAL;
	}

	pipe_id = pipe->mode;

	if (stream->started == true)
	{
		IA_CSS_WARNING("Cannot start stream that is already started");
		IA_CSS_LEAVE_ERR(err);
		return err;
	}

	pipe->stop_requested = false;

	switch (pipe_id)
	{
	case IA_CSS_PIPE_ID_PREVIEW:
		err = preview_start(pipe);
		break;
	case IA_CSS_PIPE_ID_VIDEO:
		err = video_start(pipe);
		break;
	case IA_CSS_PIPE_ID_CAPTURE:
		err = capture_start(pipe);
		break;
	case IA_CSS_PIPE_ID_YUVPP:
		err = yuvpp_start(pipe);
		break;
	case IA_CSS_PIPE_ID_ACC:
		acc_start(pipe);
		break;
	default:
		err = -EINVAL;
	}
	/* DH regular multi pipe - not continuous mode: start the next pipes too */
	if (!stream->config.continuous)
	{
		int i;

		for (i = 1; i < stream->num_pipes && 0 == err ; i++) {
			switch (stream->pipes[i]->mode) {
			case IA_CSS_PIPE_ID_PREVIEW:
				stream->pipes[i]->stop_requested = false;
				err = preview_start(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_VIDEO:
				stream->pipes[i]->stop_requested = false;
				err = video_start(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_CAPTURE:
				stream->pipes[i]->stop_requested = false;
				err = capture_start(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_YUVPP:
				stream->pipes[i]->stop_requested = false;
				err = yuvpp_start(stream->pipes[i]);
				break;
			case IA_CSS_PIPE_ID_ACC:
				stream->pipes[i]->stop_requested = false;
				acc_start(stream->pipes[i]);
				break;
			default:
				err = -EINVAL;
			}
		}
	}
	if (err)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}

	/* Force ISP parameter calculation after a mode change
	 * Acceleration API examples pass NULL for stream but they
	 * don't use ISP parameters anyway. So this should be okay.
	 * The SP binary (jpeg) copy does not use any parameters.
	 */
	if (!copy_on_sp(pipe))
	{
		sh_css_invalidate_params(stream);
		err = sh_css_param_update_isp_params(pipe,
						     stream->isp_params_configs, true, NULL);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
	}

	ia_css_debug_pipe_graph_dump_epilogue();

	ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);

	if (!sh_css_sp_is_running())
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EBUSY);
		/* SP is not running. The queues are not valid */
		return -EBUSY;
	}
	ia_css_bufq_enqueue_psys_event(IA_CSS_PSYS_SW_EVENT_START_STREAM,
				       (uint8_t)thread_id, 0, 0);

	/* DH regular multi pipe - not continuous mode: enqueue event to the next pipes too */
	if (!stream->config.continuous)
	{
		int i;

		for (i = 1; i < stream->num_pipes; i++) {
			ia_css_pipeline_get_sp_thread_id(
			    ia_css_pipe_get_pipe_num(stream->pipes[i]),
			    &thread_id);
			ia_css_bufq_enqueue_psys_event(
			    IA_CSS_PSYS_SW_EVENT_START_STREAM,
			    (uint8_t)thread_id, 0, 0);
		}
	}

	/* in case of continuous capture mode, we also start capture thread and copy thread*/
	if (pipe->stream->config.continuous)
	{
		struct ia_css_pipe *copy_pipe = NULL;

		if (pipe_id == IA_CSS_PIPE_ID_PREVIEW)
			copy_pipe = pipe->pipe_settings.preview.copy_pipe;
		else if (pipe_id == IA_CSS_PIPE_ID_VIDEO)
			copy_pipe = pipe->pipe_settings.video.copy_pipe;

		if (!copy_pipe) {
			IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
			return -EINVAL;
		}
		ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(copy_pipe),
						 &thread_id);
		/* by the time we reach here q is initialized and handle is available.*/
		ia_css_bufq_enqueue_psys_event(
		    IA_CSS_PSYS_SW_EVENT_START_STREAM,
		    (uint8_t)thread_id, 0,  0);
	}
	if (pipe->stream->cont_capt)
	{
		struct ia_css_pipe *capture_pipe = NULL;

		if (pipe_id == IA_CSS_PIPE_ID_PREVIEW)
			capture_pipe = pipe->pipe_settings.preview.capture_pipe;
		else if (pipe_id == IA_CSS_PIPE_ID_VIDEO)
			capture_pipe = pipe->pipe_settings.video.capture_pipe;

		if (!capture_pipe) {
			IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
			return -EINVAL;
		}
		ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(capture_pipe),
						 &thread_id);
		/* by the time we reach here q is initialized and handle is available.*/
		ia_css_bufq_enqueue_psys_event(
		    IA_CSS_PSYS_SW_EVENT_START_STREAM,
		    (uint8_t)thread_id, 0,  0);
	}

	/* in case of PREVIEW mode, check whether QOS acc_pipe is available, then start the qos pipe */
	if (pipe_id == IA_CSS_PIPE_ID_PREVIEW)
	{
		struct ia_css_pipe *acc_pipe = NULL;

		acc_pipe = pipe->pipe_settings.preview.acc_pipe;

		if (acc_pipe) {
			ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(acc_pipe),
							 &thread_id);
			/* by the time we reach here q is initialized and handle is available.*/
			ia_css_bufq_enqueue_psys_event(
			    IA_CSS_PSYS_SW_EVENT_START_STREAM,
			    (uint8_t)thread_id, 0, 0);
		}
	}

	stream->started = true;

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/* ISP2400 */
void
sh_css_enable_cont_capt(bool enable, bool stop_copy_preview)
{
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_enable_cont_capt() enter: enable=%d\n", enable);
//my_css.cont_capt = enable;
	my_css.stop_copy_preview = stop_copy_preview;
}

bool
sh_css_continuous_is_enabled(uint8_t pipe_num)
{
	struct ia_css_pipe *pipe;
	bool continuous;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_continuous_is_enabled() enter: pipe_num=%d\n", pipe_num);

	pipe = find_pipe_by_num(pipe_num);
	continuous = pipe && pipe->stream->config.continuous;
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "sh_css_continuous_is_enabled() leave: enable=%d\n",
			    continuous);
	return continuous;
}

/* ISP2400 */
int
ia_css_stream_get_max_buffer_depth(struct ia_css_stream *stream,
				   int *buffer_depth) {
	if (!buffer_depth)
		return -EINVAL;
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_stream_get_max_buffer_depth() enter: void\n");
	(void)stream;
	*buffer_depth = NUM_CONTINUOUS_FRAMES;
	return 0;
}

int
ia_css_stream_set_buffer_depth(struct ia_css_stream *stream, int buffer_depth) {
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_stream_set_buffer_depth() enter: num_frames=%d\n", buffer_depth);
	(void)stream;
	if (buffer_depth > NUM_CONTINUOUS_FRAMES || buffer_depth < 1)
		return -EINVAL;
	/* ok, value allowed */
	stream->config.target_num_cont_raw_buf = buffer_depth;
	/* TODO: check what to regarding initialization */
	return 0;
}

/* ISP2401 */
int
ia_css_stream_get_buffer_depth(struct ia_css_stream *stream,
			       int *buffer_depth) {
	if (!buffer_depth)
		return -EINVAL;
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_stream_get_buffer_depth() enter: void\n");
	(void)stream;
	*buffer_depth = stream->config.target_num_cont_raw_buf;
	return 0;
}

/*
 * @brief Stop all "ia_css_pipe" instances in the target
 * "ia_css_stream" instance.
 *
 * Refer to "Local prototypes" for more info.
 */
/* ISP2401 */
static int
sh_css_pipes_stop(struct ia_css_stream *stream)
{
	int err = 0;
	struct ia_css_pipe *main_pipe;
	enum ia_css_pipe_id main_pipe_id;
	int i;

	assert(stream);
	if (!stream)
	{
		IA_CSS_LOG("stream does NOT exist!");
		err = -EINVAL;
		goto ERR;
	}

	main_pipe = stream->last_pipe;
	assert(main_pipe);
	if (!main_pipe)
	{
		IA_CSS_LOG("main_pipe does NOT exist!");
		err = -EINVAL;
		goto ERR;
	}

	main_pipe_id = main_pipe->mode;
	IA_CSS_ENTER_PRIVATE("main_pipe_id=%d", main_pipe_id);

	/*
	 * Stop all "ia_css_pipe" instances in this target
	 * "ia_css_stream" instance.
	 */
	for (i = 0; i < stream->num_pipes; i++)
	{
		/* send the "stop" request to the "ia_css_pipe" instance */
		IA_CSS_LOG("Send the stop-request to the pipe: pipe_id=%d",
			stream->pipes[i]->pipeline.pipe_id);
		err = ia_css_pipeline_request_stop(&stream->pipes[i]->pipeline);

	/*
	 * Exit this loop if "ia_css_pipeline_request_stop()"
	 * returns the error code.
	 *
	 * The error code would be generated in the following
	 * two cases:
	 * (1) The Scalar Processor has already been stopped.
	 * (2) The "Host->SP" event queue is full.
	 *
	 * As the convention of using CSS API 2.0/2.1, such CSS
	 * error code would be propogated from the CSS-internal
	 * API returned value to the CSS API returned value. Then
	 * the CSS driver should capture these error code and
	 * handle it in the driver exception handling mechanism.
	 */
	if (err) {
		goto ERR;
	}
	}

	/*
	 * In the CSS firmware use scenario "Continuous Preview"
	 * as well as "Continuous Video", the "ia_css_pipe" instance
	 * "Copy Pipe" is activated. This "Copy Pipe" is private to
	 * the CSS firmware so that it is not listed in the target
	 * "ia_css_stream" instance.
	 *
	 * We need to stop this "Copy Pipe", as well.
	 */
	if (main_pipe->stream->config.continuous)
	{
		struct ia_css_pipe *copy_pipe = NULL;

		/* get the reference to "Copy Pipe" */
		if (main_pipe_id == IA_CSS_PIPE_ID_PREVIEW)
			copy_pipe = main_pipe->pipe_settings.preview.copy_pipe;
		else if (main_pipe_id == IA_CSS_PIPE_ID_VIDEO)
			copy_pipe = main_pipe->pipe_settings.video.copy_pipe;

		/* return the error code if "Copy Pipe" does NOT exist */
		assert(copy_pipe);
		if (!copy_pipe) {
			IA_CSS_LOG("Copy Pipe does NOT exist!");
			err = -EINVAL;
			goto ERR;
		}

		/* send the "stop" request to "Copy Pipe" */
		IA_CSS_LOG("Send the stop-request to the pipe: pipe_id=%d",
			copy_pipe->pipeline.pipe_id);
		err = ia_css_pipeline_request_stop(&copy_pipe->pipeline);
	}

ERR:
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/*
 * @brief Check if all "ia_css_pipe" instances in the target
 * "ia_css_stream" instance have stopped.
 *
 * Refer to "Local prototypes" for more info.
 */
/* ISP2401 */
static bool
sh_css_pipes_have_stopped(struct ia_css_stream *stream)
{
	bool rval = true;

	struct ia_css_pipe *main_pipe;
	enum ia_css_pipe_id main_pipe_id;

	int i;

	assert(stream);
	if (!stream) {
		IA_CSS_LOG("stream does NOT exist!");
		rval = false;
		goto RET;
	}

	main_pipe = stream->last_pipe;
	assert(main_pipe);

	if (!main_pipe) {
		IA_CSS_LOG("main_pipe does NOT exist!");
		rval = false;
		goto RET;
	}

	main_pipe_id = main_pipe->mode;
	IA_CSS_ENTER_PRIVATE("main_pipe_id=%d", main_pipe_id);

	/*
	 * Check if every "ia_css_pipe" instance in this target
	 * "ia_css_stream" instance has stopped.
	 */
	for (i = 0; i < stream->num_pipes; i++) {
		rval = rval && ia_css_pipeline_has_stopped(&stream->pipes[i]->pipeline);
		IA_CSS_LOG("Pipe has stopped: pipe_id=%d, stopped=%d",
			   stream->pipes[i]->pipeline.pipe_id,
			   rval);
	}

	/*
	 * In the CSS firmware use scenario "Continuous Preview"
	 * as well as "Continuous Video", the "ia_css_pipe" instance
	 * "Copy Pipe" is activated. This "Copy Pipe" is private to
	 * the CSS firmware so that it is not listed in the target
	 * "ia_css_stream" instance.
	 *
	 * We need to check if this "Copy Pipe" has stopped, as well.
	 */
	if (main_pipe->stream->config.continuous) {
		struct ia_css_pipe *copy_pipe = NULL;

		/* get the reference to "Copy Pipe" */
		if (main_pipe_id == IA_CSS_PIPE_ID_PREVIEW)
			copy_pipe = main_pipe->pipe_settings.preview.copy_pipe;
		else if (main_pipe_id == IA_CSS_PIPE_ID_VIDEO)
			copy_pipe = main_pipe->pipe_settings.video.copy_pipe;

		/* return if "Copy Pipe" does NOT exist */
		assert(copy_pipe);
		if (!copy_pipe) {
			IA_CSS_LOG("Copy Pipe does NOT exist!");

			rval = false;
			goto RET;
		}

		/* check if "Copy Pipe" has stopped or not */
		rval = rval && ia_css_pipeline_has_stopped(&copy_pipe->pipeline);
		IA_CSS_LOG("Pipe has stopped: pipe_id=%d, stopped=%d",
			   copy_pipe->pipeline.pipe_id,
			   rval);
	}

RET:
	IA_CSS_LEAVE_PRIVATE("rval=%d", rval);
	return rval;
}

#if !defined(HAS_NO_INPUT_SYSTEM) && defined(USE_INPUT_SYSTEM_VERSION_2)
unsigned int
sh_css_get_mipi_sizes_for_check(const unsigned int port, const unsigned int idx)
{
	OP___assert(port < N_CSI_PORTS);
	OP___assert(idx  < IA_CSS_MIPI_SIZE_CHECK_MAX_NOF_ENTRIES_PER_PORT);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_get_mipi_sizes_for_check(port %d, idx %d): %d\n",
			    port, idx, my_css.mipi_sizes_for_check[port][idx]);
	return my_css.mipi_sizes_for_check[port][idx];
}
#endif

static int sh_css_pipe_configure_output(
    struct ia_css_pipe *pipe,
    unsigned int width,
    unsigned int height,
    unsigned int padded_width,
    enum ia_css_frame_format format,
    unsigned int idx)
{
	int err = 0;

	IA_CSS_ENTER_PRIVATE("pipe = %p, width = %d, height = %d, padded width = %d, format = %d, idx = %d",
			     pipe, width, height, padded_width, format, idx);
	if (!pipe) {
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	err = ia_css_util_check_res(width, height);
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}
	if (pipe->output_info[idx].res.width != width ||
	    pipe->output_info[idx].res.height != height ||
	    pipe->output_info[idx].format != format) {
		ia_css_frame_info_init(
		    &pipe->output_info[idx],
		    width,
		    height,
		    format,
		    padded_width);
	}
	IA_CSS_LEAVE_ERR_PRIVATE(0);
	return 0;
}

static int
sh_css_pipe_get_shading_info(struct ia_css_pipe *pipe,
			     struct ia_css_shading_info *shading_info,
			     struct ia_css_pipe_config *pipe_config)
{
	int err = 0;
	struct ia_css_binary *binary = NULL;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_pipe_get_shading_info() enter:\n");

	binary = ia_css_pipe_get_shading_correction_binary(pipe);

	if (binary)
	{
		err = ia_css_binary_get_shading_info(binary,
						     IA_CSS_SHADING_CORRECTION_TYPE_1,
						     pipe->required_bds_factor,
						     (const struct ia_css_stream_config *)&pipe->stream->config,
						     shading_info, pipe_config);

		/* Other function calls can be added here when other shading correction types will be added
		 * in the future.
		 */
	} else
	{
		/* When the pipe does not have a binary which has the shading
		 * correction, this function does not need to fill the shading
		 * information. It is not a error case, and then
		 * this function should return 0.
		 */
		memset(shading_info, 0, sizeof(*shading_info));
	}
	return err;
}

static int
sh_css_pipe_get_grid_info(struct ia_css_pipe *pipe,
			  struct ia_css_grid_info *info) {
	int err = 0;
	struct ia_css_binary *binary = NULL;

	assert(pipe);
	assert(info);

	IA_CSS_ENTER_PRIVATE("");

	binary = ia_css_pipe_get_s3a_binary(pipe);

	if (binary)
	{
		err = ia_css_binary_3a_grid_info(binary, info, pipe);
		if (err)
			goto ERR;
	} else
		memset(&info->s3a_grid, 0, sizeof(info->s3a_grid));

	binary = ia_css_pipe_get_sdis_binary(pipe);

	if (binary)
	{
		ia_css_binary_dvs_grid_info(binary, info, pipe);
		ia_css_binary_dvs_stat_grid_info(binary, info, pipe);
	} else
	{
		memset(&info->dvs_grid.dvs_grid_info, 0,
		       sizeof(info->dvs_grid.dvs_grid_info));
		memset(&info->dvs_grid.dvs_stat_grid_info, 0,
		       sizeof(info->dvs_grid.dvs_stat_grid_info));
	}

	if (binary)
	{
		/* copy pipe does not have ISP binary*/
		info->isp_in_width = binary->internal_frame_info.res.width;
		info->isp_in_height = binary->internal_frame_info.res.height;
	}

#if defined(HAS_VAMEM_VERSION_2)
	info->vamem_type = IA_CSS_VAMEM_TYPE_2;
#elif defined(HAS_VAMEM_VERSION_1)
	info->vamem_type = IA_CSS_VAMEM_TYPE_1;
#else
#error "Unknown VAMEM version"
#endif

ERR :
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

/* ISP2401 */
/*
 * @brief Check if a format is supported by the pipe.
 *
 */
static int
ia_css_pipe_check_format(struct ia_css_pipe *pipe,
			 enum ia_css_frame_format format) {
	const enum ia_css_frame_format *supported_formats;
	int number_of_formats;
	int found = 0;
	int i;

	IA_CSS_ENTER_PRIVATE("");

	if (NULL == pipe || NULL == pipe->pipe_settings.video.video_binary.info)
	{
		IA_CSS_ERROR("Pipe or binary info is not set");
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	supported_formats = pipe->pipe_settings.video.video_binary.info->output_formats;
	number_of_formats = sizeof(pipe->pipe_settings.video.video_binary.info->output_formats) / sizeof(enum ia_css_frame_format);

	for (i = 0; i < number_of_formats && !found; i++)
	{
		if (supported_formats[i] == format) {
			found = 1;
			break;
		}
	}
	if (!found)
	{
		IA_CSS_ERROR("Requested format is not supported by binary");
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	} else
	{
		IA_CSS_LEAVE_ERR_PRIVATE(0);
		return 0;
	}
}

static int load_video_binaries(struct ia_css_pipe *pipe)
{
	struct ia_css_frame_info video_in_info, tnr_info,
		       *video_vf_info, video_bds_out_info, *pipe_out_info, *pipe_vf_out_info;
	bool online;
	int err = 0;
	bool continuous = pipe->stream->config.continuous;
	unsigned int i;
	unsigned int num_output_pins;
	struct ia_css_frame_info video_bin_out_info;
	bool need_scaler = false;
	bool vf_res_different_than_output = false;
	bool need_vf_pp = false;
	int vf_ds_log2;
	struct ia_css_video_settings *mycs  = &pipe->pipe_settings.video;

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_VIDEO);
	/* we only test the video_binary because offline video doesn't need a
	 * vf_pp binary and online does not (always use) the copy_binary.
	 * All are always reset at the same time anyway.
	 */
	if (mycs->video_binary.info)
		return 0;

	online = pipe->stream->config.online;
	pipe_out_info = &pipe->output_info[0];
	pipe_vf_out_info = &pipe->vf_output_info[0];

	assert(pipe_out_info);

	/*
	 * There is no explicit input format requirement for raw or yuv
	 * What matters is that there is a binary that supports the stream format.
	 * This is checked in the binary_find(), so no need to check it here
	 */
	err = ia_css_util_check_input(&pipe->stream->config, false, false);
	if (err)
		return err;
	/* cannot have online video and input_mode memory */
	if (online && pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY)
		return -EINVAL;
	if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0]) {
		err = ia_css_util_check_vf_out_info(pipe_out_info,
						    pipe_vf_out_info);
		if (err)
			return err;
	} else {
		err = ia_css_frame_check_info(pipe_out_info);
		if (err)
			return err;
	}

	if (pipe->out_yuv_ds_input_info.res.width)
		video_bin_out_info = pipe->out_yuv_ds_input_info;
	else
		video_bin_out_info = *pipe_out_info;

	/* Video */
	if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0]) {
		video_vf_info = pipe_vf_out_info;
		vf_res_different_than_output = (video_vf_info->res.width !=
						video_bin_out_info.res.width) ||
					       (video_vf_info->res.height != video_bin_out_info.res.height);
	} else {
		video_vf_info = NULL;
	}

	need_scaler = need_downscaling(video_bin_out_info.res, pipe_out_info->res);

	/* we build up the pipeline starting at the end */
	/* YUV post-processing if needed */
	if (need_scaler) {
		struct ia_css_cas_binary_descr cas_scaler_descr = { };

		/* NV12 is the common format that is supported by both */
		/* yuv_scaler and the video_xx_isp2_min binaries. */
		video_bin_out_info.format = IA_CSS_FRAME_FORMAT_NV12;

		err = ia_css_pipe_create_cas_scaler_desc_single_output(
			  &video_bin_out_info,
			  pipe_out_info,
			  NULL,
			  &cas_scaler_descr);
		if (err)
			return err;
		mycs->num_yuv_scaler = cas_scaler_descr.num_stage;
		mycs->yuv_scaler_binary = kzalloc(cas_scaler_descr.num_stage *
						  sizeof(struct ia_css_binary), GFP_KERNEL);
		if (!mycs->yuv_scaler_binary) {
			err = -ENOMEM;
			return err;
		}
		mycs->is_output_stage = kzalloc(cas_scaler_descr.num_stage
						* sizeof(bool), GFP_KERNEL);
		if (!mycs->is_output_stage) {
			err = -ENOMEM;
			return err;
		}
		for (i = 0; i < cas_scaler_descr.num_stage; i++) {
			struct ia_css_binary_descr yuv_scaler_descr;

			mycs->is_output_stage[i] = cas_scaler_descr.is_output_stage[i];
			ia_css_pipe_get_yuvscaler_binarydesc(pipe,
							     &yuv_scaler_descr, &cas_scaler_descr.in_info[i],
							     &cas_scaler_descr.out_info[i],
							     &cas_scaler_descr.internal_out_info[i],
							     &cas_scaler_descr.vf_info[i]);
			err = ia_css_binary_find(&yuv_scaler_descr,
						 &mycs->yuv_scaler_binary[i]);
			if (err) {
				kfree(mycs->is_output_stage);
				mycs->is_output_stage = NULL;
				return err;
			}
		}
		ia_css_pipe_destroy_cas_scaler_desc(&cas_scaler_descr);
	}

	{
		struct ia_css_binary_descr video_descr;
		enum ia_css_frame_format vf_info_format;

		err = ia_css_pipe_get_video_binarydesc(pipe,
						       &video_descr, &video_in_info, &video_bds_out_info, &video_bin_out_info,
						       video_vf_info,
						       pipe->stream->config.left_padding);
		if (err)
			return err;

		/* In the case where video_vf_info is not NULL, this allows
		 * us to find a potential video library with desired vf format.
		 * If success, no vf_pp binary is needed.
		 * If failed, we will look up video binary with YUV_LINE vf format
		 */
		err = ia_css_binary_find(&video_descr,
					 &mycs->video_binary);

		if (err) {
			if (video_vf_info) {
				/* This will do another video binary lookup later for YUV_LINE format*/
				need_vf_pp = true;
			} else
				return err;
		} else if (video_vf_info) {
			/* The first video binary lookup is successful, but we may
			 * still need vf_pp binary based on additiona check */
			num_output_pins = mycs->video_binary.info->num_output_pins;
			vf_ds_log2 = mycs->video_binary.vf_downscale_log2;

			/* If the binary has dual output pins, we need vf_pp if the resolution
			* is different. */
			need_vf_pp |= ((num_output_pins == 2) && vf_res_different_than_output);

			/* If the binary has single output pin, we need vf_pp if additional
			* scaling is needed for vf */
			need_vf_pp |= ((num_output_pins == 1) &&
				       ((video_vf_info->res.width << vf_ds_log2 != pipe_out_info->res.width) ||
					(video_vf_info->res.height << vf_ds_log2 != pipe_out_info->res.height)));
		}

		if (need_vf_pp) {
			/* save the current vf_info format for restoration later */
			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
					    "load_video_binaries() need_vf_pp; find video binary with YUV_LINE again\n");

			vf_info_format = video_vf_info->format;

			if (!pipe->config.enable_vfpp_bci)
				ia_css_frame_info_set_format(video_vf_info,
							     IA_CSS_FRAME_FORMAT_YUV_LINE);

			ia_css_binary_destroy_isp_parameters(&mycs->video_binary);

			err = ia_css_binary_find(&video_descr,
						 &mycs->video_binary);

			/* restore original vf_info format */
			ia_css_frame_info_set_format(video_vf_info,
						     vf_info_format);
			if (err)
				return err;
		}
	}

	/* If a video binary does not use a ref_frame, we set the frame delay
	 * to 0. This is the case for the 1-stage low-power video binary. */
	if (!mycs->video_binary.info->sp.enable.ref_frame)
		pipe->dvs_frame_delay = 0;

	/* The delay latency determines the number of invalid frames after
	 * a stream is started. */
	pipe->num_invalid_frames = pipe->dvs_frame_delay;
	pipe->info.num_invalid_frames = pipe->num_invalid_frames;

	/* Viewfinder frames also decrement num_invalid_frames. If the pipe
	 * outputs a viewfinder output, then we need double the number of
	 * invalid frames */
	if (video_vf_info)
		pipe->num_invalid_frames *= 2;

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
			    "load_video_binaries() num_invalid_frames=%d dvs_frame_delay=%d\n",
			    pipe->num_invalid_frames, pipe->dvs_frame_delay);

	/* pqiao TODO: temp hack for PO, should be removed after offline YUVPP is enabled */
#if !defined(USE_INPUT_SYSTEM_VERSION_2401)
	/* Copy */
	if (!online && !continuous) {
		/* TODO: what exactly needs doing, prepend the copy binary to
		 *	 video base this only on !online?
		 */
		err = load_copy_binary(pipe,
				       &mycs->copy_binary,
				       &mycs->video_binary);
		if (err)
			return err;
	}
#else
	(void)continuous;
#endif

#if !defined(HAS_OUTPUT_SYSTEM)
	if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && need_vf_pp) {
		struct ia_css_binary_descr vf_pp_descr;

		if (mycs->video_binary.vf_frame_info.format
		    == IA_CSS_FRAME_FORMAT_YUV_LINE) {
			ia_css_pipe_get_vfpp_binarydesc(pipe, &vf_pp_descr,
							&mycs->video_binary.vf_frame_info,
							pipe_vf_out_info);
		} else {
			/* output from main binary is not yuv line. currently this is
			 * possible only when bci is enabled on vfpp output */
			assert(pipe->config.enable_vfpp_bci == true);
			ia_css_pipe_get_yuvscaler_binarydesc(pipe, &vf_pp_descr,
							     &mycs->video_binary.vf_frame_info,
							     pipe_vf_out_info, NULL, NULL);
		}

		err = ia_css_binary_find(&vf_pp_descr,
					 &mycs->vf_pp_binary);
		if (err)
			return err;
	}
#endif

	err = allocate_delay_frames(pipe);

	if (err)
		return err;

	if (mycs->video_binary.info->sp.enable.block_output) {
		unsigned int tnr_width;
		unsigned int tnr_height;

		tnr_info = mycs->video_binary.out_frame_info[0];

		if (IS_ISP2401) {
			/* Select resolution for TNR. If
			* output_system_in_resolution(GDC_out_resolution) is
			* being used, then select that as it will also be in resolution for
			* TNR. At present, it only make sense for Skycam */
			if (pipe->config.output_system_in_res.width &&
			    pipe->config.output_system_in_res.height) {
				tnr_width = pipe->config.output_system_in_res.width;
				tnr_height = pipe->config.output_system_in_res.height;
			} else {
				tnr_width = tnr_info.res.width;
				tnr_height = tnr_info.res.height;
			}

			/* Make tnr reference buffers output block width(in pix) align */
			tnr_info.res.width  = CEIL_MUL(tnr_width,
						       (mycs->video_binary.info->sp.block.block_width * ISP_NWAY));
			tnr_info.padded_width = tnr_info.res.width;
		} else {
			tnr_height = tnr_info.res.height;
		}

		/* Make tnr reference buffers output block height align */
		tnr_info.res.height = CEIL_MUL(tnr_height,
					       mycs->video_binary.info->sp.block.output_block_height);
	} else {
		tnr_info = mycs->video_binary.internal_frame_info;
	}
	tnr_info.format = IA_CSS_FRAME_FORMAT_YUV_LINE;
	tnr_info.raw_bit_depth = SH_CSS_TNR_BIT_DEPTH;

	for (i = 0; i < NUM_TNR_FRAMES; i++) {
		if (mycs->tnr_frames[i]) {
			ia_css_frame_free(mycs->tnr_frames[i]);
			mycs->tnr_frames[i] = NULL;
		}
		err = ia_css_frame_allocate_from_info(
			  &mycs->tnr_frames[i],
			  &tnr_info);
		if (err)
			return err;
	}
	IA_CSS_LEAVE_PRIVATE("");
	return 0;
}

static int
unload_video_binaries(struct ia_css_pipe *pipe) {
	unsigned int i;

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

	if ((!pipe) || (pipe->mode != IA_CSS_PIPE_ID_VIDEO))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}
	ia_css_binary_unload(&pipe->pipe_settings.video.copy_binary);
	ia_css_binary_unload(&pipe->pipe_settings.video.video_binary);
	ia_css_binary_unload(&pipe->pipe_settings.video.vf_pp_binary);

	for (i = 0; i < pipe->pipe_settings.video.num_yuv_scaler; i++)
		ia_css_binary_unload(&pipe->pipe_settings.video.yuv_scaler_binary[i]);

	kfree(pipe->pipe_settings.video.is_output_stage);
	pipe->pipe_settings.video.is_output_stage = NULL;
	kfree(pipe->pipe_settings.video.yuv_scaler_binary);
	pipe->pipe_settings.video.yuv_scaler_binary = NULL;

	IA_CSS_LEAVE_ERR_PRIVATE(0);
	return 0;
}

static int video_start(struct ia_css_pipe *pipe)
{
	struct ia_css_binary *copy_binary;
	int err = 0;
	struct ia_css_pipe *copy_pipe, *capture_pipe;
	enum sh_css_pipe_config_override copy_ovrd;
	enum ia_css_input_mode video_pipe_input_mode;

	const struct ia_css_coordinate *coord = NULL;
	const struct ia_css_isp_parameters *params = NULL;

	IA_CSS_ENTER_PRIVATE("pipe = %p", pipe);
	if ((!pipe) || (pipe->mode != IA_CSS_PIPE_ID_VIDEO)) {
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	video_pipe_input_mode = pipe->stream->config.mode;

	copy_pipe    = pipe->pipe_settings.video.copy_pipe;
	capture_pipe = pipe->pipe_settings.video.capture_pipe;

	copy_binary  = &pipe->pipe_settings.video.copy_binary;

	sh_css_metrics_start_frame();

	/* multi stream video needs mipi buffers */

#if defined(USE_INPUT_SYSTEM_VERSION_2) || defined(USE_INPUT_SYSTEM_VERSION_2401)
	err = send_mipi_frames(pipe);
	if (err)
		return err;
#endif

	send_raw_frames(pipe);
	{
		unsigned int thread_id;

		ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id);
		copy_ovrd = 1 << thread_id;

		if (pipe->stream->cont_capt) {
			ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(capture_pipe),
							 &thread_id);
			copy_ovrd |= 1 << thread_id;
		}
	}

	if (IS_ISP2401) {
		coord = &pipe->config.internal_frame_origin_bqs_on_sctbl;
		params = pipe->stream->isp_params_configs;
	}

	/* Construct and load the copy pipe */
	if (pipe->stream->config.continuous) {
		sh_css_sp_init_pipeline(&copy_pipe->pipeline,
					IA_CSS_PIPE_ID_COPY,
					(uint8_t)ia_css_pipe_get_pipe_num(copy_pipe),
					false,
					pipe->stream->config.pixels_per_clock == 2, false,
					false, pipe->required_bds_factor,
					copy_ovrd,
					pipe->stream->config.mode,
					&pipe->stream->config.metadata_config,
					&pipe->stream->info.metadata_info,
#if !defined(HAS_NO_INPUT_SYSTEM)
					pipe->stream->config.source.port.port,
#endif
					coord,
					params);

		/* make the video pipe start with mem mode input, copy handles
		   the actual mode */
		video_pipe_input_mode = IA_CSS_INPUT_MODE_MEMORY;
	}

	/* Construct and load the capture pipe */
	if (pipe->stream->cont_capt) {
		sh_css_sp_init_pipeline(&capture_pipe->pipeline,
					IA_CSS_PIPE_ID_CAPTURE,
					(uint8_t)ia_css_pipe_get_pipe_num(capture_pipe),
					capture_pipe->config.default_capture_config.enable_xnr != 0,
					capture_pipe->stream->config.pixels_per_clock == 2,
					true, /* continuous */
					false, /* offline */
					capture_pipe->required_bds_factor,
					0,
					IA_CSS_INPUT_MODE_MEMORY,
					&pipe->stream->config.metadata_config,
					&pipe->stream->info.metadata_info,
#if !defined(HAS_NO_INPUT_SYSTEM)
					(enum mipi_port_id)0,
#endif
					coord,
					params);
	}

	start_pipe(pipe, copy_ovrd, video_pipe_input_mode);

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static
int sh_css_pipe_get_viewfinder_frame_info(
    struct ia_css_pipe *pipe,
    struct ia_css_frame_info *info,
    unsigned int idx)
{
	assert(pipe);
	assert(info);

	/* We could print the pointer as input arg, and the values as output */
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_pipe_get_viewfinder_frame_info() enter: void\n");

	if (pipe->mode == IA_CSS_PIPE_ID_CAPTURE &&
	    (pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_RAW ||
	     pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_BAYER))
		return -EINVAL;
	/* offline video does not generate viewfinder output */
	*info = pipe->vf_output_info[idx];

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_pipe_get_viewfinder_frame_info() leave: \
		info.res.width=%d, info.res.height=%d, \
		info.padded_width=%d, info.format=%d, \
		info.raw_bit_depth=%d, info.raw_bayer_order=%d\n",
			    info->res.width, info->res.height,
			    info->padded_width, info->format,
			    info->raw_bit_depth, info->raw_bayer_order);

	return 0;
}

static int
sh_css_pipe_configure_viewfinder(struct ia_css_pipe *pipe, unsigned int width,
				 unsigned int height, unsigned int min_width,
				 enum ia_css_frame_format format,
				 unsigned int idx) {
	int err = 0;

	IA_CSS_ENTER_PRIVATE("pipe = %p, width = %d, height = %d, min_width = %d, format = %d, idx = %d\n",
			     pipe, width, height, min_width, format, idx);

	if (!pipe)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}

	err = ia_css_util_check_res(width, height);
	if (err)
	{
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}
	if (pipe->vf_output_info[idx].res.width != width ||
	    pipe->vf_output_info[idx].res.height != height ||
	    pipe->vf_output_info[idx].format != format)
	{
		ia_css_frame_info_init(&pipe->vf_output_info[idx], width, height,
				       format, min_width);
	}
	IA_CSS_LEAVE_ERR_PRIVATE(0);
	return 0;
}

static int load_copy_binaries(struct ia_css_pipe *pipe)
{
	int err = 0;

	assert(pipe);
	IA_CSS_ENTER_PRIVATE("");

	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE ||
	       pipe->mode == IA_CSS_PIPE_ID_COPY);
	if (pipe->pipe_settings.capture.copy_binary.info)
		return 0;

	err = ia_css_frame_check_info(&pipe->output_info[0]);
	if (err)
		goto ERR;

	err = verify_copy_out_frame_format(pipe);
	if (err)
		goto ERR;

	err = load_copy_binary(pipe,
			       &pipe->pipe_settings.capture.copy_binary,
			       NULL);

ERR:
	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static bool need_capture_pp(
    const struct ia_css_pipe *pipe)
{
	const struct ia_css_frame_info *out_info = &pipe->output_info[0];

	IA_CSS_ENTER_LEAVE_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE);

	if (IS_ISP2401) {
		/* ldc and capture_pp are not supported in the same pipeline */
		if (need_capt_ldc(pipe) == true)
			return false;
	}

	/* determine whether we need to use the capture_pp binary.
	 * This is needed for:
	 *   1. XNR or
	 *   2. Digital Zoom or
	 *   3. YUV downscaling
	 */
	if (pipe->out_yuv_ds_input_info.res.width &&
	    ((pipe->out_yuv_ds_input_info.res.width != out_info->res.width) ||
	     (pipe->out_yuv_ds_input_info.res.height != out_info->res.height)))
		return true;

	if (pipe->config.default_capture_config.enable_xnr != 0)
		return true;

	if ((pipe->stream->isp_params_configs->dz_config.dx < HRT_GDC_N) ||
	    (pipe->stream->isp_params_configs->dz_config.dy < HRT_GDC_N) ||
	    pipe->config.enable_dz)
		return true;

	return false;
}

static bool need_capt_ldc(
    const struct ia_css_pipe *pipe)
{
	IA_CSS_ENTER_LEAVE_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE);
	return (pipe->extra_config.enable_dvs_6axis) ? true : false;
}

static int set_num_primary_stages(unsigned int *num,
	enum ia_css_pipe_version version)
{
	int err = 0;

	if (!num)
		return -EINVAL;

	switch (version) {
	case IA_CSS_PIPE_VERSION_2_6_1:
		*num = NUM_PRIMARY_HQ_STAGES;
		break;
	case IA_CSS_PIPE_VERSION_2_2:
	case IA_CSS_PIPE_VERSION_1:
		*num = NUM_PRIMARY_STAGES;
		break;
	default:
		err = -EINVAL;
		break;
	}

	return err;
}

static int load_primary_binaries(
    struct ia_css_pipe *pipe)
{
	bool online = false;
	bool memory = false;
	bool continuous = false;
	bool need_pp = false;
	bool need_isp_copy_binary = false;
	bool need_ldc = false;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	bool sensor = false;
#endif
	struct ia_css_frame_info prim_in_info,
		       prim_out_info,
		       capt_pp_out_info, vf_info,
		       *vf_pp_in_info, *pipe_out_info,
		       *pipe_vf_out_info, *capt_pp_in_info,
		       capt_ldc_out_info;
	int err = 0;
	struct ia_css_capture_settings *mycs;
	unsigned int i;
	bool need_extra_yuv_scaler = false;
	struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES];

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->stream);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE ||
	       pipe->mode == IA_CSS_PIPE_ID_COPY);

	online = pipe->stream->config.online;
	memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY;
	continuous = pipe->stream->config.continuous;
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR);
#endif

	mycs = &pipe->pipe_settings.capture;
	pipe_out_info = &pipe->output_info[0];
	pipe_vf_out_info = &pipe->vf_output_info[0];

	if (mycs->primary_binary[0].info)
		return 0;

	err = set_num_primary_stages(&mycs->num_primary_stage,
				     pipe->config.isp_pipe_version);
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}

	if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0]) {
		err = ia_css_util_check_vf_out_info(pipe_out_info, pipe_vf_out_info);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
	} else {
		err = ia_css_frame_check_info(pipe_out_info);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
	}
	need_pp = need_capture_pp(pipe);

	/* we use the vf output info to get the primary/capture_pp binary
	   configured for vf_veceven. It will select the closest downscaling
	   factor. */
	vf_info = *pipe_vf_out_info;

	/*
	 * WARNING: The #if def flag has been added below as a
	 * temporary solution to solve the problem of enabling the
	 * view finder in a single binary in a capture flow. The
	 * vf-pp stage has been removed for Skycam in the solution
	 * provided. The vf-pp stage should be re-introduced when
	 * required. This should not be considered as a clean solution.
	 * Proper investigation should be done to come up with the clean
	 * solution.
	 * */
	ia_css_frame_info_set_format(&vf_info, IA_CSS_FRAME_FORMAT_YUV_LINE);

	/* TODO: All this yuv_scaler and capturepp calculation logic
	 * can be shared later. Capture_pp is also a yuv_scale binary
	 * with extra XNR funcionality. Therefore, it can be made as the
	 * first step of the cascade. */
	capt_pp_out_info = pipe->out_yuv_ds_input_info;
	capt_pp_out_info.format = IA_CSS_FRAME_FORMAT_YUV420;
	capt_pp_out_info.res.width  /= MAX_PREFERRED_YUV_DS_PER_STEP;
	capt_pp_out_info.res.height /= MAX_PREFERRED_YUV_DS_PER_STEP;
	ia_css_frame_info_set_width(&capt_pp_out_info, capt_pp_out_info.res.width, 0);

	need_extra_yuv_scaler = need_downscaling(capt_pp_out_info.res,
						 pipe_out_info->res);

	if (need_extra_yuv_scaler) {
		struct ia_css_cas_binary_descr cas_scaler_descr = { };

		err = ia_css_pipe_create_cas_scaler_desc_single_output(
			  &capt_pp_out_info,
			  pipe_out_info,
			  NULL,
			  &cas_scaler_descr);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
		mycs->num_yuv_scaler = cas_scaler_descr.num_stage;
		mycs->yuv_scaler_binary = kzalloc(cas_scaler_descr.num_stage *
						  sizeof(struct ia_css_binary), GFP_KERNEL);
		if (!mycs->yuv_scaler_binary) {
			err = -ENOMEM;
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
		mycs->is_output_stage = kzalloc(cas_scaler_descr.num_stage *
						sizeof(bool), GFP_KERNEL);
		if (!mycs->is_output_stage) {
			err = -ENOMEM;
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
		for (i = 0; i < cas_scaler_descr.num_stage; i++) {
			struct ia_css_binary_descr yuv_scaler_descr;

			mycs->is_output_stage[i] = cas_scaler_descr.is_output_stage[i];
			ia_css_pipe_get_yuvscaler_binarydesc(pipe,
							     &yuv_scaler_descr, &cas_scaler_descr.in_info[i],
							     &cas_scaler_descr.out_info[i],
							     &cas_scaler_descr.internal_out_info[i],
							     &cas_scaler_descr.vf_info[i]);
			err = ia_css_binary_find(&yuv_scaler_descr,
						 &mycs->yuv_scaler_binary[i]);
			if (err) {
				IA_CSS_LEAVE_ERR_PRIVATE(err);
				return err;
			}
		}
		ia_css_pipe_destroy_cas_scaler_desc(&cas_scaler_descr);

	} else {
		capt_pp_out_info = pipe->output_info[0];
	}

	/* TODO Do we disable ldc for skycam */
	need_ldc = need_capt_ldc(pipe);
	if (IS_ISP2401 && need_ldc) {
		/* ldc and capt_pp are not supported in the same pipeline */
		struct ia_css_binary_descr capt_ldc_descr;

		ia_css_pipe_get_ldc_binarydesc(pipe,
					       &capt_ldc_descr, &prim_out_info,
					       &capt_pp_out_info);

		err = ia_css_binary_find(&capt_ldc_descr,
					 &mycs->capture_ldc_binary);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
		need_pp = 0;
		need_ldc = 0;
	}

	/* we build up the pipeline starting at the end */
	/* Capture post-processing */
	if (need_pp) {
		struct ia_css_binary_descr capture_pp_descr;

		if (!IS_ISP2401)
			capt_pp_in_info = need_ldc ? &capt_ldc_out_info : &prim_out_info;
		else
			capt_pp_in_info = &prim_out_info;

		ia_css_pipe_get_capturepp_binarydesc(pipe,
							&capture_pp_descr, capt_pp_in_info,
							&capt_pp_out_info, &vf_info);
		err = ia_css_binary_find(&capture_pp_descr,
					    &mycs->capture_pp_binary);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}

		if (need_ldc) {
			struct ia_css_binary_descr capt_ldc_descr;

			ia_css_pipe_get_ldc_binarydesc(pipe,
							&capt_ldc_descr, &prim_out_info,
							&capt_ldc_out_info);

			err = ia_css_binary_find(&capt_ldc_descr,
						    &mycs->capture_ldc_binary);
			if (err) {
				IA_CSS_LEAVE_ERR_PRIVATE(err);
				return err;
			}
		}
	} else {
		prim_out_info = *pipe_out_info;
	}

	/* Primary */
	for (i = 0; i < mycs->num_primary_stage; i++) {
		struct ia_css_frame_info *local_vf_info = NULL;

		if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] &&
		    (i == mycs->num_primary_stage - 1))
			local_vf_info = &vf_info;
		ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], &prim_in_info,
						    &prim_out_info, local_vf_info, i);
		err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
	}

	/* Viewfinder post-processing */
	if (need_pp)
		vf_pp_in_info = &mycs->capture_pp_binary.vf_frame_info;
	else
		vf_pp_in_info = &mycs->primary_binary[mycs->num_primary_stage - 1].vf_frame_info;

	/*
	    * WARNING: The #if def flag has been added below as a
	    * temporary solution to solve the problem of enabling the
	    * view finder in a single binary in a capture flow. The
	    * vf-pp stage has been removed for Skycam in the solution
	    * provided. The vf-pp stage should be re-introduced when
	    * required. Thisshould not be considered as a clean solution.
	    * Proper  * investigation should be done to come up with the clean
	    * solution.
	    * */
	if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0]) {
		struct ia_css_binary_descr vf_pp_descr;

		ia_css_pipe_get_vfpp_binarydesc(pipe,
						&vf_pp_descr, vf_pp_in_info, pipe_vf_out_info);
		err = ia_css_binary_find(&vf_pp_descr, &mycs->vf_pp_binary);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
	}
	err = allocate_delay_frames(pipe);

	if (err)
		return err;

#ifdef USE_INPUT_SYSTEM_VERSION_2401
	/* When the input system is 2401, only the Direct Sensor Mode
	    * Offline Capture uses the ISP copy binary.
	    */
	need_isp_copy_binary = !online && sensor;
#else
	need_isp_copy_binary = !online && !continuous && !memory;
#endif

	/* ISP Copy */
	if (need_isp_copy_binary) {
		err = load_copy_binary(pipe,
					&mycs->copy_binary,
					&mycs->primary_binary[0]);
		if (err) {
			IA_CSS_LEAVE_ERR_PRIVATE(err);
			return err;
		}
	}

	return 0;
}

static int
allocate_delay_frames(struct ia_css_pipe *pipe) {
	unsigned int num_delay_frames = 0, i = 0;
	unsigned int dvs_frame_delay = 0;
	struct ia_css_frame_info ref_info;
	int err = 0;
	enum ia_css_pipe_id mode = IA_CSS_PIPE_ID_VIDEO;
	struct ia_css_frame **delay_frames = NULL;

	IA_CSS_ENTER_PRIVATE("");

	if (!pipe)
	{
		IA_CSS_ERROR("Invalid args - pipe %p", pipe);
		return -EINVAL;
	}

	mode = pipe->mode;
	dvs_frame_delay = pipe->dvs_frame_delay;

	if (dvs_frame_delay > 0)
		num_delay_frames = dvs_frame_delay + 1;

	switch (mode)
	{
	case IA_CSS_PIPE_ID_CAPTURE: {
		struct ia_css_capture_settings *mycs_capture = &pipe->pipe_settings.capture;
		(void)mycs_capture;
		return err;
	}
	break;
	case IA_CSS_PIPE_ID_VIDEO: {
		struct ia_css_video_settings *mycs_video = &pipe->pipe_settings.video;

		ref_info = mycs_video->video_binary.internal_frame_info;
		/*The ref frame expects
		    *	1. Y plane
		    *	2. UV plane with line interleaving, like below
		    *		UUUUUU(width/2 times) VVVVVVVV..(width/2 times)
		    *
		    *	This format is not YUV420(which has Y, U and V planes).
		    *	Its closer to NV12, except that the UV plane has UV
		    *	interleaving, like UVUVUVUVUVUVUVUVU...
		    *
		    *	TODO: make this ref_frame format as a separate frame format
		    */
		ref_info.format        = IA_CSS_FRAME_FORMAT_NV12;
		delay_frames = mycs_video->delay_frames;
	}
	break;
	case IA_CSS_PIPE_ID_PREVIEW: {
		struct ia_css_preview_settings *mycs_preview = &pipe->pipe_settings.preview;

		ref_info = mycs_preview->preview_binary.internal_frame_info;
		/*The ref frame expects
		    *	1. Y plane
		    *	2. UV plane with line interleaving, like below
		    *		UUUUUU(width/2 times) VVVVVVVV..(width/2 times)
		    *
		    *	This format is not YUV420(which has Y, U and V planes).
		    *	Its closer to NV12, except that the UV plane has UV
		    *	interleaving, like UVUVUVUVUVUVUVUVU...
		    *
		    *	TODO: make this ref_frame format as a separate frame format
		    */
		ref_info.format        = IA_CSS_FRAME_FORMAT_NV12;
		delay_frames = mycs_preview->delay_frames;
	}
	break;
	default:
		return -EINVAL;
	}

	ref_info.raw_bit_depth = SH_CSS_REF_BIT_DEPTH;

	assert(num_delay_frames <= MAX_NUM_VIDEO_DELAY_FRAMES);
	for (i = 0; i < num_delay_frames; i++)
	{
		err = ia_css_frame_allocate_from_info(&delay_frames[i],	&ref_info);
		if (err)
			return err;
	}
	IA_CSS_LEAVE_PRIVATE("");
	return 0;
}

static int load_advanced_binaries(
    struct ia_css_pipe *pipe) {
	struct ia_css_frame_info pre_in_info, gdc_in_info,
			post_in_info, post_out_info,
			vf_info, *vf_pp_in_info, *pipe_out_info,
			*pipe_vf_out_info;
	bool need_pp;
	bool need_isp_copy = true;
	int err = 0;

	IA_CSS_ENTER_PRIVATE("");

	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE ||
		pipe->mode == IA_CSS_PIPE_ID_COPY);
	if (pipe->pipe_settings.capture.pre_isp_binary.info)
		return 0;
	pipe_out_info = &pipe->output_info[0];
	pipe_vf_out_info = &pipe->vf_output_info[0];

	vf_info = *pipe_vf_out_info;
	err = ia_css_util_check_vf_out_info(pipe_out_info, &vf_info);
	if (err)
		return err;
	need_pp = need_capture_pp(pipe);

	ia_css_frame_info_set_format(&vf_info,
					IA_CSS_FRAME_FORMAT_YUV_LINE);

	/* we build up the pipeline starting at the end */
	/* Capture post-processing */
	if (need_pp) {
		struct ia_css_binary_descr capture_pp_descr;

		ia_css_pipe_get_capturepp_binarydesc(pipe,
							&capture_pp_descr, &post_out_info, pipe_out_info, &vf_info);
		err = ia_css_binary_find(&capture_pp_descr,
					    &pipe->pipe_settings.capture.capture_pp_binary);
		if (err)
			return err;
	} else {
		post_out_info = *pipe_out_info;
	}

	/* Post-gdc */
	{
		struct ia_css_binary_descr post_gdc_descr;

		ia_css_pipe_get_post_gdc_binarydesc(pipe,
						    &post_gdc_descr, &post_in_info, &post_out_info, &vf_info);
		err = ia_css_binary_find(&post_gdc_descr,
					    &pipe->pipe_settings.capture.post_isp_binary);
		if (err)
			return err;
	}

	/* Gdc */
	{
		struct ia_css_binary_descr gdc_descr;

		ia_css_pipe_get_gdc_binarydesc(pipe, &gdc_descr, &gdc_in_info,
						&pipe->pipe_settings.capture.post_isp_binary.in_frame_info);
		err = ia_css_binary_find(&gdc_descr,
					    &pipe->pipe_settings.capture.anr_gdc_binary);
		if (err)
			return err;
	}
	pipe->pipe_settings.capture.anr_gdc_binary.left_padding =
	    pipe->pipe_settings.capture.post_isp_binary.left_padding;

	/* Pre-gdc */
	{
		struct ia_css_binary_descr pre_gdc_descr;

		ia_css_pipe_get_pre_gdc_binarydesc(pipe, &pre_gdc_descr, &pre_in_info,
						    &pipe->pipe_settings.capture.anr_gdc_binary.in_frame_info);
		err = ia_css_binary_find(&pre_gdc_descr,
					    &pipe->pipe_settings.capture.pre_isp_binary);
		if (err)
			return err;
	}
	pipe->pipe_settings.capture.pre_isp_binary.left_padding =
	    pipe->pipe_settings.capture.anr_gdc_binary.left_padding;

	/* Viewfinder post-processing */
	if (need_pp) {
		vf_pp_in_info =
		    &pipe->pipe_settings.capture.capture_pp_binary.vf_frame_info;
	} else {
		vf_pp_in_info =
		    &pipe->pipe_settings.capture.post_isp_binary.vf_frame_info;
	}

	{
		struct ia_css_binary_descr vf_pp_descr;

		ia_css_pipe_get_vfpp_binarydesc(pipe,
						&vf_pp_descr, vf_pp_in_info, pipe_vf_out_info);
		err = ia_css_binary_find(&vf_pp_descr,
					    &pipe->pipe_settings.capture.vf_pp_binary);
		if (err)
			return err;
	}

	/* Copy */
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	/* For CSI2+, only the direct sensor mode/online requires ISP copy */
	need_isp_copy = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR;
#endif
	if (need_isp_copy)
		load_copy_binary(pipe,
				    &pipe->pipe_settings.capture.copy_binary,
				    &pipe->pipe_settings.capture.pre_isp_binary);

	return err;
}

static int load_bayer_isp_binaries(
    struct ia_css_pipe *pipe) {
	struct ia_css_frame_info pre_isp_in_info, *pipe_out_info;
	int err = 0;
	struct ia_css_binary_descr pre_de_descr;

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE ||
		pipe->mode == IA_CSS_PIPE_ID_COPY);
	pipe_out_info = &pipe->output_info[0];

	if (pipe->pipe_settings.capture.pre_isp_binary.info)
		return 0;

	err = ia_css_frame_check_info(pipe_out_info);
	if (err)
		return err;

	ia_css_pipe_get_pre_de_binarydesc(pipe, &pre_de_descr,
					    &pre_isp_in_info,
					    pipe_out_info);

	err = ia_css_binary_find(&pre_de_descr,
				    &pipe->pipe_settings.capture.pre_isp_binary);

	return err;
}

static int load_low_light_binaries(
    struct ia_css_pipe *pipe) {
	struct ia_css_frame_info pre_in_info, anr_in_info,
			post_in_info, post_out_info,
			vf_info, *pipe_vf_out_info, *pipe_out_info,
			*vf_pp_in_info;
	bool need_pp;
	bool need_isp_copy = true;
	int err = 0;

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE ||
		pipe->mode == IA_CSS_PIPE_ID_COPY);

	if (pipe->pipe_settings.capture.pre_isp_binary.info)
		return 0;
	pipe_vf_out_info = &pipe->vf_output_info[0];
	pipe_out_info = &pipe->output_info[0];

	vf_info = *pipe_vf_out_info;
	err = ia_css_util_check_vf_out_info(pipe_out_info,
					    &vf_info);
	if (err)
		return err;
	need_pp = need_capture_pp(pipe);

	ia_css_frame_info_set_format(&vf_info,
					IA_CSS_FRAME_FORMAT_YUV_LINE);

	/* we build up the pipeline starting at the end */
	/* Capture post-processing */
	if (need_pp) {
		struct ia_css_binary_descr capture_pp_descr;

		ia_css_pipe_get_capturepp_binarydesc(pipe,
							&capture_pp_descr, &post_out_info, pipe_out_info, &vf_info);
		err = ia_css_binary_find(&capture_pp_descr,
					    &pipe->pipe_settings.capture.capture_pp_binary);
		if (err)
			return err;
	} else {
		post_out_info = *pipe_out_info;
	}

	/* Post-anr */
	{
		struct ia_css_binary_descr post_anr_descr;

		ia_css_pipe_get_post_anr_binarydesc(pipe,
						    &post_anr_descr, &post_in_info, &post_out_info, &vf_info);
		err = ia_css_binary_find(&post_anr_descr,
					    &pipe->pipe_settings.capture.post_isp_binary);
		if (err)
			return err;
	}

	/* Anr */
	{
		struct ia_css_binary_descr anr_descr;

		ia_css_pipe_get_anr_binarydesc(pipe, &anr_descr, &anr_in_info,
						&pipe->pipe_settings.capture.post_isp_binary.in_frame_info);
		err = ia_css_binary_find(&anr_descr,
					    &pipe->pipe_settings.capture.anr_gdc_binary);
		if (err)
			return err;
	}
	pipe->pipe_settings.capture.anr_gdc_binary.left_padding =
	    pipe->pipe_settings.capture.post_isp_binary.left_padding;

	/* Pre-anr */
	{
		struct ia_css_binary_descr pre_anr_descr;

		ia_css_pipe_get_pre_anr_binarydesc(pipe, &pre_anr_descr, &pre_in_info,
						    &pipe->pipe_settings.capture.anr_gdc_binary.in_frame_info);
		err = ia_css_binary_find(&pre_anr_descr,
					    &pipe->pipe_settings.capture.pre_isp_binary);
		if (err)
			return err;
	}
	pipe->pipe_settings.capture.pre_isp_binary.left_padding =
	    pipe->pipe_settings.capture.anr_gdc_binary.left_padding;

	/* Viewfinder post-processing */
	if (need_pp) {
		vf_pp_in_info =
		    &pipe->pipe_settings.capture.capture_pp_binary.vf_frame_info;
	} else {
		vf_pp_in_info =
		    &pipe->pipe_settings.capture.post_isp_binary.vf_frame_info;
	}

	{
		struct ia_css_binary_descr vf_pp_descr;

		ia_css_pipe_get_vfpp_binarydesc(pipe,
						&vf_pp_descr, vf_pp_in_info, pipe_vf_out_info);
		err = ia_css_binary_find(&vf_pp_descr,
					    &pipe->pipe_settings.capture.vf_pp_binary);
		if (err)
			return err;
	}

	/* Copy */
#ifdef USE_INPUT_SYSTEM_VERSION_2401
	/* For CSI2+, only the direct sensor mode/online requires ISP copy */
	need_isp_copy = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR;
#endif
	if (need_isp_copy)
		err = load_copy_binary(pipe,
					&pipe->pipe_settings.capture.copy_binary,
					&pipe->pipe_settings.capture.pre_isp_binary);

	return err;
}

static bool copy_on_sp(struct ia_css_pipe *pipe)
{
	bool rval;

	assert(pipe);
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "copy_on_sp() enter:\n");

	rval = true;

	rval &=	(pipe->mode == IA_CSS_PIPE_ID_CAPTURE);

	rval &= (pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_RAW);

	rval &= ((pipe->stream->config.input_config.format ==
		    ATOMISP_INPUT_FORMAT_BINARY_8) ||
		    (pipe->config.mode == IA_CSS_PIPE_MODE_COPY));

	return rval;
}

static int load_capture_binaries(
    struct ia_css_pipe *pipe) {
	int err = 0;
	bool must_be_raw;

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_CAPTURE ||
		pipe->mode == IA_CSS_PIPE_ID_COPY);

	if (pipe->pipe_settings.capture.primary_binary[0].info) {
		IA_CSS_LEAVE_ERR_PRIVATE(0);
		return 0;
	}

	/* in primary, advanced,low light or bayer,
						the input format must be raw */
	must_be_raw =
	    pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_ADVANCED ||
	    pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_BAYER ||
	    pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_LOW_LIGHT;
	err = ia_css_util_check_input(&pipe->stream->config, must_be_raw, false);
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}
	if (copy_on_sp(pipe) &&
	    pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) {
		ia_css_frame_info_init(
		    &pipe->output_info[0],
		    JPEG_BYTES,
		    1,
		    IA_CSS_FRAME_FORMAT_BINARY_8,
		    0);
		IA_CSS_LEAVE_ERR_PRIVATE(0);
		return 0;
	}

	switch (pipe->config.default_capture_config.mode) {
	case IA_CSS_CAPTURE_MODE_RAW:
		err = load_copy_binaries(pipe);
#if !defined(HAS_NO_INPUT_SYSTEM) && defined(USE_INPUT_SYSTEM_VERSION_2401)
		if (!err)
			pipe->pipe_settings.capture.copy_binary.online = pipe->stream->config.online;
#endif
		break;
	case IA_CSS_CAPTURE_MODE_BAYER:
		err = load_bayer_isp_binaries(pipe);
		break;
	case IA_CSS_CAPTURE_MODE_PRIMARY:
		err = load_primary_binaries(pipe);
		break;
	case IA_CSS_CAPTURE_MODE_ADVANCED:
		err = load_advanced_binaries(pipe);
		break;
	case IA_CSS_CAPTURE_MODE_LOW_LIGHT:
		err = load_low_light_binaries(pipe);
		break;
	}
	if (err) {
		IA_CSS_LEAVE_ERR_PRIVATE(err);
		return err;
	}

	IA_CSS_LEAVE_ERR_PRIVATE(err);
	return err;
}

static int
unload_capture_binaries(struct ia_css_pipe *pipe) {
	unsigned int i;

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

	if ((!pipe) || ((pipe->mode != IA_CSS_PIPE_ID_CAPTURE) && (pipe->mode != IA_CSS_PIPE_ID_COPY)))
	{
		IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
		return -EINVAL;
	}
	ia_css_binary_unload(&pipe->pipe_settings.capture.copy_binary);
	for (i = 0; i < MAX_NUM_PRIMARY_STAGES; i++)
		ia_css_binary_unload(&pipe->pipe_settings.capture.primary_binary[i]);
	ia_css_binary_unload(&pipe->pipe_settings.capture.pre_isp_binary);
	ia_css_binary_unload(&pipe->pipe_settings.capture.anr_gdc_binary);
	ia_css_binary_unload(&pipe->pipe_settings.capture.post_isp_binary);
	ia_css_binary_unload(&pipe->pipe_settings.capture.capture_pp_binary);
	ia_css_binary_unload(&pipe->pipe_settings.capture.capture_ldc_binary);
	ia_css_binary_unload(&pipe->pipe_settings.capture.vf_pp_binary);

	for (i = 0; i < pipe->pipe_settings.capture.num_yuv_scaler; i++)
		ia_css_binary_unload(&pipe->pipe_settings.capture.yuv_scaler_binary[i]);

	kfree(pipe->pipe_settings.capture.is_output_stage);
	pipe->pipe_settings.capture.is_output_stage = NULL;
	kfree(pipe->pipe_settings.capture.yuv_scaler_binary);
	pipe->pipe_settings.capture.yuv_scaler_binary = NULL;

	IA_CSS_LEAVE_ERR_PRIVATE(0);
	return 0;
}

static bool
need_downscaling(const struct ia_css_resolution in_res,
		    const struct ia_css_resolution out_res) {
	if (in_res.width > out_res.width || in_res.height > out_res.height)
		return true;

	return false;
}

static bool
need_yuv_scaler_stage(const struct ia_css_pipe *pipe) {
	unsigned int i;
	struct ia_css_resolution in_res, out_res;

	bool need_format_conversion = false;

	IA_CSS_ENTER_PRIVATE("");
	assert(pipe);
	assert(pipe->mode == IA_CSS_PIPE_ID_YUVPP);

	/* TODO: make generic function */
	need_format_conversion =
	    ((pipe->stream->config.input_config.format ==
		ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) &&
		(pipe->output_info[0].format != IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8));

	in_res = pipe->config.input_effective_res;

	if (pipe->config.enable_dz)
		return true;

	if ((pipe->output_info[0].res.width != 0) && need_format_conversion)
		return true;

	for (i = 0; i < IA_CSS_PIPE_MAX_OUTPUT_STAGE; i++) {
		out_res = pipe->output_info[i].res;

		/* A non-zero width means it is a valid output port */
		if ((out_res.width != 0) && need_downscaling(in_res, out_res))
			return true;
	}

	return false;
}

/* TODO: it is temporarily created from ia_css_pipe_create_cas_scaler_desc */
/* which has some hard-coded knowledge which prevents reuse of the function. */
/* Later, merge this with ia_css_pipe_create_cas_scaler_desc */
static int ia_css_pipe_create_cas_scaler_desc_single_output(
    struct ia_css_frame_info *cas_scaler_in_info,
    struct ia_css_frame_info *cas_scaler_out_info,
    struct ia_css_frame_info *cas_scaler_vf_info,
    struct ia_css_cas_binary_descr *descr) {
	unsigned int i;
	unsigned int hor_ds_factor = 0, ver_ds_factor = 0;
	int err = 0;
	struct ia_css_frame_info tmp_in_info;

	unsigned int max_scale_factor_per_stage = MAX_PREFERRED_YUV_DS_PER_STEP;

	assert(cas_scaler_in_info);
	assert(cas_scaler_out_info);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "ia_css_pipe_create_cas_scaler_desc() enter:\n");

	/* We assume that this function is used only for single output port case. */
	descr->num_output_stage = 1;

	hor_ds_factor = CEIL_DIV(cas_scaler_in_info->res.width,
				    cas_scaler_out_info->res.width);
	ver_ds_factor = CEIL_DIV(cas_scaler_in_info->res.height,
				    cas_scaler_out_info->res.height);
	/* use the same horizontal and vertical downscaling factor for simplicity */
	assert(hor_ds_factor == ver_ds_factor);

	i = 1;
	while (i < hor_ds_factor) {
		descr->num_stage++;
		i *= max_scale_factor_per_stage;
	}

	descr->in_info = kmalloc(descr->num_stage * sizeof(struct ia_css_frame_info),
				    GFP_KERNEL);
	if (!descr->in_info) {
		err = -ENOMEM;
		goto ERR;
	}
	descr->internal_out_info = kmalloc(descr->num_stage * sizeof(
						struct ia_css_frame_info), GFP_KERNEL);
	if (!descr->internal_out_info) {
		err = -ENOMEM;
		goto ERR;
	}
	descr->out_info = kmalloc(descr->num_stage * sizeof(struct ia_css_frame_info),
				    GFP_KERNEL);
	if (!descr->out_info) {
		err = -ENOMEM;
		goto ERR;
	}
	descr->vf_info = kmalloc(descr->num_stage * sizeof(struct ia_css_frame_info),
				    GFP_KERNEL);
	if (!descr->vf_info) {
		err = -ENOMEM;
		goto ERR;
	}
	descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), GFP_KERNEL);
	if (!descr->is_output_stage) {
		err = -ENOMEM;
		goto ERR;
	}

	tmp_in_info = *cas_scaler_in_info;
	for (i = 0; i < descr->num_stage; i++) {
		descr->in_info[i] = tmp_in_info;
		if ((tmp_in_info.res.width / max_scale_factor_per_stage) <=
		    cas_scaler_out_info->res.width) {
			descr->is_output_stage[i] = true;
			if ((descr->num_output_stage > 1) && (i != (descr->num_stage - 1))) {
				descr->internal_out_info[i].res.width = cas_scaler_out_info->res.width;
				descr->internal_out_info[i].res.height = cas_scaler_out_info->res.height;
				descr->internal_out_info[i].padded_width = cas_scaler_out_info->padded_width;
				descr->internal_out_info[i].format = IA_CSS_FRAME_FORMAT_YUV420;
			} else {
				assert(i == (descr->num_stage - 1));
				descr->internal_out_info[i].res.width = 0;
				descr->internal_out_info[i].res.height = 0;
			}
			descr->out_info[i].res.width = cas_scaler_out_info->res.width;
			descr->out_info[i].res.height = cas_scaler_out_info->res.height;
			descr->out_info[i].padded_width = cas_scaler_out_info->padded_width;
			descr->out_info[i].format = cas_scaler_out_info->format;
			if (cas_scaler_vf_info) {
				descr->vf_info[i].res.width = cas_scaler_vf_info->res.width;
				descr->vf_info[i].res.height = cas_scaler_vf_info->res.height;
				descr->vf_info[i].padded_width = cas_scaler_vf_info->padded_width;
				ia_css_frame_info_set_format(&descr->vf_info[i], IA_CSS_FRAME_FORMAT_YUV_LINE);
			} else {
				descr->vf_info[i].res.width = 0;
				descr->vf_info[i].res.height = 0;
				descr->vf_info[i].padded_width = 0;
			}
		} else {
			descr->is_output_stage[i] = false;
			descr->internal_out_info[i].res.width = tmp_in_info.res.width /
								max_scale_factor_per_stage;
			descr->internal_out_info[i].res.height = tmp_in_info.res.height /
				max_scale_factor_per_stage;
			descr->internal_out_info[i].format = IA_CSS_FRAME_FORMAT_YUV420;
			ia_css_frame_info_init(&descr->internal_out_info[i],
						tmp_in_info.res.width / max_scale_factor_per_stage,
						tmp_in_info.res.height / max_scale_factor_per_stage,
						IA_CSS_FRAME_FORMAT_YUV420, 0);
			descr->out_info[i].res.width = 0;
			descr->out_info[i].res.height = 0;
			descr->vf_info[i].res.width = 0;
			descr->vf_info[i].res.height = 0;
		}
		tmp_in_info = descr->internal_out_info[i];
	}
ERR:
	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "ia_css_pipe_create_cas_scaler_desc() leave, err=%d\n",
			    err);
	return err;
}

/* FIXME: merge most of this and single output version */
static int ia_css_pipe_create_cas_scaler_desc(
    struct ia_css_pipe *pipe,
    struct ia_css_cas_binary_descr *descr) {
	struct ia_css_frame_info in_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO;
	struct ia_css_frame_info *out_info[IA_CSS_PIPE_MAX_OUTPUT_STAGE];
	struct ia_css_frame_info *vf_out_info[IA_CSS_PIPE_MAX_OUTPUT_STAGE];
	struct ia_css_frame_info tmp_in_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO;
	unsigned int i, j;
	unsigned int hor_scale_factor[IA_CSS_PIPE_MAX_OUTPUT_STAGE],
		    ver_scale_factor[IA_CSS_PIPE_MAX_OUTPUT_STAGE],
		    scale_factor = 0;
	unsig