All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yong Zhi <yong.zhi@intel.com>
To: linux-media@vger.kernel.org, sakari.ailus@linux.intel.com
Cc: jian.xu.zheng@intel.com, tfiga@chromium.org,
	rajmohan.mani@intel.com, tuukka.toivonen@intel.com,
	hyungwoo.yang@intel.com, chiranjeevi.rapolu@intel.com,
	jerry.w.hu@intel.com, Yong Zhi <yong.zhi@intel.com>
Subject: [PATCH v5 10/12] intel-ipu3: Add css pipeline programming
Date: Fri,  1 Dec 2017 22:32:20 -0600	[thread overview]
Message-ID: <1512189142-19863-11-git-send-email-yong.zhi@intel.com> (raw)
In-Reply-To: <1512189142-19863-1-git-send-email-yong.zhi@intel.com>

This provides helper library to be used by
v4l2 level to program imaging pipelines and
control the streaming.

Signed-off-by: Yong Zhi <yong.zhi@intel.com>
---
 drivers/media/pci/intel/ipu3/ipu3-css.c | 1780 +++++++++++++++++++++++++++++++
 1 file changed, 1780 insertions(+)

diff --git a/drivers/media/pci/intel/ipu3/ipu3-css.c b/drivers/media/pci/intel/ipu3/ipu3-css.c
index 1e61ecb64334..b720588a2ce5 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-css.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-css.c
@@ -26,6 +26,177 @@
 				 IMGU_IRQCTRL_IRQ_SW_PIN(0) | \
 				 IMGU_IRQCTRL_IRQ_SW_PIN(1))
 
+#define IPU3_CSS_FORMAT_BPP_DEN	50	/* Denominator */
+
+/* Some sane limits for resolutions */
+#define IPU3_CSS_MIN_RES	32
+#define IPU3_CSS_MAX_H		3136
+#define IPU3_CSS_MAX_W		4224
+
+/* filter size from graph settings is fixed as 4 */
+#define FILTER_SIZE             4
+#define MIN_ENVELOPE            8
+
+/*
+ * pre-allocated buffer size for CSS ABI, auxiliary frames
+ * after BDS and before GDC. Those values should be tuned
+ * to big enough to avoid buffer re-allocation when
+ * streaming to lower streaming latency.
+ */
+#define CSS_ABI_SIZE    136
+#define CSS_BDS_SIZE    (4480 * 3200 * 3)
+#define CSS_GDC_SIZE    (4224 * 3200 * 12 / 8)
+
+#define IPU3_CSS_QUEUE_TO_FLAGS(q)	(1 << (q))
+#define IPU3_CSS_FORMAT_FL_IN		\
+			IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_IN)
+#define IPU3_CSS_FORMAT_FL_OUT		\
+			IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_OUT)
+#define IPU3_CSS_FORMAT_FL_VF		\
+			IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_VF)
+
+/* Formats supported by IPU3 Camera Sub System */
+static const struct ipu3_css_format ipu3_css_formats[] = {
+	{
+		.pixelformat = V4L2_PIX_FMT_NV12,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.frame_format = IMGU_ABI_FRAME_FORMAT_NV12,
+		.osys_format = IMGU_ABI_OSYS_FORMAT_NV12,
+		.osys_tiling = IMGU_ABI_OSYS_TILING_NONE,
+		.bytesperpixel_num = 1 * IPU3_CSS_FORMAT_BPP_DEN,
+		.chroma_decim = 4,
+		.width_align = IPU3_UAPI_ISP_VEC_ELEMS,
+		.flags = IPU3_CSS_FORMAT_FL_OUT | IPU3_CSS_FORMAT_FL_VF,
+	}, {
+		/* Each 32 bytes contains 25 10-bit pixels */
+		.pixelformat = V4L2_PIX_FMT_IPU3_SBGGR10,
+		.colorspace = V4L2_COLORSPACE_RAW,
+		.frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED,
+		.bayer_order = IMGU_ABI_BAYER_ORDER_BGGR,
+		.bit_depth = 10,
+		.bytesperpixel_num = 64,
+		.width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS,
+		.flags = IPU3_CSS_FORMAT_FL_IN,
+	}, {
+		.pixelformat = V4L2_PIX_FMT_IPU3_SGBRG10,
+		.colorspace = V4L2_COLORSPACE_RAW,
+		.frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED,
+		.bayer_order = IMGU_ABI_BAYER_ORDER_GBRG,
+		.bit_depth = 10,
+		.bytesperpixel_num = 64,
+		.width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS,
+		.flags = IPU3_CSS_FORMAT_FL_IN,
+	}, {
+		.pixelformat = V4L2_PIX_FMT_IPU3_SGRBG10,
+		.colorspace = V4L2_COLORSPACE_RAW,
+		.frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED,
+		.bayer_order = IMGU_ABI_BAYER_ORDER_GRBG,
+		.bit_depth = 10,
+		.bytesperpixel_num = 64,
+		.width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS,
+		.flags = IPU3_CSS_FORMAT_FL_IN,
+	}, {
+		.pixelformat = V4L2_PIX_FMT_IPU3_SRGGB10,
+		.colorspace = V4L2_COLORSPACE_RAW,
+		.frame_format = IMGU_ABI_FRAME_FORMAT_RAW_PACKED,
+		.bayer_order = IMGU_ABI_BAYER_ORDER_RGGB,
+		.bit_depth = 10,
+		.bytesperpixel_num = 64,
+		.width_align = 2 * IPU3_UAPI_ISP_VEC_ELEMS,
+		.flags = IPU3_CSS_FORMAT_FL_IN,
+	},
+};
+
+static const struct {
+	enum imgu_abi_queue_id qid;
+	size_t ptr_ofs;
+} ipu3_css_queues[IPU3_CSS_QUEUES] = {
+	[IPU3_CSS_QUEUE_IN] = {
+		IMGU_ABI_QUEUE_C_ID,
+		offsetof(struct imgu_abi_buffer, payload.frame.frame_data)
+	},
+	[IPU3_CSS_QUEUE_OUT] = {
+		IMGU_ABI_QUEUE_D_ID,
+		offsetof(struct imgu_abi_buffer, payload.frame.frame_data)
+	},
+	[IPU3_CSS_QUEUE_VF] = {
+		IMGU_ABI_QUEUE_E_ID,
+		offsetof(struct imgu_abi_buffer, payload.frame.frame_data)
+	},
+	[IPU3_CSS_QUEUE_STAT_3A] = {
+		IMGU_ABI_QUEUE_F_ID,
+		offsetof(struct imgu_abi_buffer, payload.s3a.data_ptr)
+	},
+	[IPU3_CSS_QUEUE_STAT_DVS] = {
+		IMGU_ABI_QUEUE_G_ID,
+		offsetof(struct imgu_abi_buffer, payload.skc_dvs_statistics)
+	}
+};
+
+/* Initialize queue based on given format, adjust format as needed */
+static int ipu3_css_queue_init(struct ipu3_css_queue *queue,
+			       struct v4l2_pix_format_mplane *fmt, u32 flags)
+{
+	struct v4l2_pix_format_mplane *const f = &queue->fmt.mpix;
+	unsigned int i;
+	u32 sizeimage;
+
+	INIT_LIST_HEAD(&queue->bufs);
+
+	queue->css_fmt = NULL;	/* Disable */
+	if (!fmt)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(ipu3_css_formats); i++) {
+		if (!(ipu3_css_formats[i].flags & flags))
+			continue;
+		queue->css_fmt = &ipu3_css_formats[i];
+		if (ipu3_css_formats[i].pixelformat == fmt->pixelformat)
+			break;
+	}
+	if (!queue->css_fmt)
+		return -EINVAL;	/* Could not find any suitable format */
+
+	queue->fmt.mpix = *fmt;
+
+	f->width = ALIGN(clamp_t(u32, f->width,
+				 IPU3_CSS_MIN_RES, IPU3_CSS_MAX_W), 2);
+	f->height = ALIGN(clamp_t(u32, f->height,
+				  IPU3_CSS_MIN_RES, IPU3_CSS_MAX_H), 2);
+	queue->width_pad = ALIGN(f->width, queue->css_fmt->width_align);
+	if (queue->css_fmt->frame_format != IMGU_ABI_FRAME_FORMAT_RAW_PACKED)
+		f->plane_fmt[0].bytesperline = DIV_ROUND_UP(queue->width_pad *
+					queue->css_fmt->bytesperpixel_num,
+					IPU3_CSS_FORMAT_BPP_DEN);
+	else
+		/* For packed raw, alignment for bpl is by 50 to the width */
+		f->plane_fmt[0].bytesperline =
+				DIV_ROUND_UP(f->width,
+					     IPU3_CSS_FORMAT_BPP_DEN) *
+					     queue->css_fmt->bytesperpixel_num;
+
+	sizeimage = f->height * f->plane_fmt[0].bytesperline;
+	if (queue->css_fmt->chroma_decim)
+		sizeimage += 2 * sizeimage / queue->css_fmt->chroma_decim;
+
+	f->plane_fmt[0].sizeimage = sizeimage;
+	f->field = V4L2_FIELD_NONE;
+	f->num_planes = 1;
+	f->colorspace = queue->css_fmt->colorspace;
+	f->flags = 0;
+	f->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	f->quantization = V4L2_QUANTIZATION_DEFAULT;
+	f->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+	memset(f->reserved, 0, sizeof(f->reserved));
+
+	return 0;
+}
+
+static bool ipu3_css_queue_enabled(struct ipu3_css_queue *q)
+{
+	return q->css_fmt;
+}
+
 /******************* css hw *******************/
 
 /* In the style of writesl() defined in include/asm-generic/io.h */
@@ -477,6 +648,1615 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css)
 	usleep_range(200, 300);
 }
 
+static void ipu3_css_pipeline_cleanup(struct ipu3_css *css)
+{
+	struct imgu_fw_info *bi = &css->fwp->binary_header[css->current_binary];
+	const unsigned int pipe = 0;
+	unsigned int i;
+
+	ipu3_css_pool_cleanup(css->dev, &css->pool.parameter_set_info);
+	ipu3_css_pool_cleanup(css->dev, &css->pool.acc);
+	ipu3_css_pool_cleanup(css->dev, &css->pool.gdc);
+	ipu3_css_pool_cleanup(css->dev, &css->pool.obgrid);
+
+	for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
+		ipu3_css_pool_cleanup(css->dev, &css->pool.binary_params_p[i]);
+
+	for (i = 0; i < bi->info.isp.sp.iterator.num_stripes; i++)
+		ipu3_dmamap_free(css->dev, &css->dvs_meta_data[pipe][i]);
+}
+
+/*
+ * This function initializes various stages of the
+ * IPU3 CSS ISP pipeline
+ */
+static int ipu3_css_pipeline_init(struct ipu3_css *css)
+{
+	static const unsigned int PIPE_ID = IPU3_CSS_PIPE_ID_VIDEO;
+	static const int BYPC = 2;	/* Bytes per component */
+	static const struct imgu_abi_buffer_sp buffer_sp_init = {
+		.buf_src = {.queue_id = IMGU_ABI_QUEUE_EVENT_ID},
+		.buf_type = IMGU_ABI_BUFFER_TYPE_INVALID,
+	};
+
+	struct imgu_abi_isp_iterator_config *cfg_iter;
+	struct imgu_abi_isp_ref_config *cfg_ref;
+	struct imgu_abi_isp_dvs_config *cfg_dvs;
+	struct imgu_abi_isp_tnr3_config *cfg_tnr;
+	struct imgu_abi_isp_ref_dmem_state *cfg_ref_state;
+	struct imgu_abi_isp_tnr3_dmem_state *cfg_tnr_state;
+
+	const int pipe = 0, stage = 0, thread = 0;
+	unsigned int i, j;
+
+	const struct imgu_fw_info *bi =
+				&css->fwp->binary_header[css->current_binary];
+	const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes;
+
+	struct imgu_fw_config_memory_offsets *cofs = (void *)css->fwp +
+	    bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_CONFIG];
+	struct imgu_fw_state_memory_offsets *sofs = (void *)css->fwp +
+	    bi->blob.memory_offsets.offsets[IMGU_ABI_PARAM_CLASS_STATE];
+
+	struct imgu_abi_isp_stage *isp_stage;
+	struct imgu_abi_sp_stage *sp_stage;
+	struct imgu_abi_sp_group *sp_group;
+
+	const unsigned int bds_width_pad =
+				ALIGN(css->rect[IPU3_CSS_RECT_BDS].width,
+				      2 * IPU3_UAPI_ISP_VEC_ELEMS);
+
+	const enum imgu_abi_memories m0 = IMGU_ABI_MEM_ISP_DMEM0;
+	enum imgu_abi_param_class cfg = IMGU_ABI_PARAM_CLASS_CONFIG;
+	void *vaddr = css->binary_params_cs[cfg - 1][m0].vaddr;
+
+	/* Configure iterator */
+
+	cfg_iter = ipu3_css_fw_pipeline_params(css, cfg, m0,
+					       &cofs->dmem.iterator,
+					       sizeof(*cfg_iter), vaddr);
+	if (!cfg_iter)
+		goto bad_firmware;
+
+	cfg_iter->input_info.res.width =
+				css->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.width;
+	cfg_iter->input_info.res.height =
+				css->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.height;
+	cfg_iter->input_info.padded_width =
+				css->queue[IPU3_CSS_QUEUE_IN].width_pad;
+	cfg_iter->input_info.format =
+			css->queue[IPU3_CSS_QUEUE_IN].css_fmt->frame_format;
+	cfg_iter->input_info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_IN].css_fmt->bit_depth;
+	cfg_iter->input_info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order;
+	cfg_iter->input_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+
+	cfg_iter->internal_info.res.width = css->rect[IPU3_CSS_RECT_BDS].width;
+	cfg_iter->internal_info.res.height =
+					css->rect[IPU3_CSS_RECT_BDS].height;
+	cfg_iter->internal_info.padded_width = bds_width_pad;
+	cfg_iter->internal_info.format =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format;
+	cfg_iter->internal_info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth;
+	cfg_iter->internal_info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order;
+	cfg_iter->internal_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+
+	cfg_iter->output_info.res.width =
+				css->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width;
+	cfg_iter->output_info.res.height =
+				css->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height;
+	cfg_iter->output_info.padded_width =
+				css->queue[IPU3_CSS_QUEUE_OUT].width_pad;
+	cfg_iter->output_info.format =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format;
+	cfg_iter->output_info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth;
+	cfg_iter->output_info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order;
+	cfg_iter->output_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+
+	cfg_iter->vf_info.res.width =
+			css->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width;
+	cfg_iter->vf_info.res.height =
+			css->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height;
+	cfg_iter->vf_info.padded_width =
+			css->queue[IPU3_CSS_QUEUE_VF].width_pad;
+	cfg_iter->vf_info.format =
+			css->queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format;
+	cfg_iter->vf_info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_VF].css_fmt->bit_depth;
+	cfg_iter->vf_info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_VF].css_fmt->bayer_order;
+	cfg_iter->vf_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+
+	cfg_iter->dvs_envelope.width = css->rect[IPU3_CSS_RECT_ENVELOPE].width;
+	cfg_iter->dvs_envelope.height =
+				css->rect[IPU3_CSS_RECT_ENVELOPE].height;
+
+	/* Configure reference (delay) frames */
+
+	cfg_ref = ipu3_css_fw_pipeline_params(css, cfg, m0, &cofs->dmem.ref,
+					      sizeof(*cfg_ref), vaddr);
+	if (!cfg_ref)
+		goto bad_firmware;
+
+	cfg_ref->port_b.crop = 0;
+	cfg_ref->port_b.elems = IMGU_ABI_ISP_DDR_WORD_BYTES / BYPC;
+	cfg_ref->port_b.width = css->aux_frames[IPU3_CSS_AUX_FRAME_REF].width;
+	cfg_ref->port_b.stride =
+			css->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline;
+	cfg_ref->width_a_over_b =
+				IPU3_UAPI_ISP_VEC_ELEMS / cfg_ref->port_b.elems;
+	cfg_ref->dvs_frame_delay = IPU3_CSS_AUX_FRAMES - 1;
+	for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) {
+		cfg_ref->ref_frame_addr_y[i] =
+			css->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i].daddr;
+		cfg_ref->ref_frame_addr_c[i] =
+			css->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i].daddr +
+			css->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline *
+			css->aux_frames[IPU3_CSS_AUX_FRAME_REF].height;
+	}
+	for (; i < IMGU_ABI_FRAMES_REF; i++) {
+		cfg_ref->ref_frame_addr_y[i] = 0;
+		cfg_ref->ref_frame_addr_c[i] = 0;
+	}
+
+	/* Configure DVS (digital video stabilization) */
+
+	cfg_dvs = ipu3_css_fw_pipeline_params(css, cfg, m0,
+					      &cofs->dmem.dvs, sizeof(*cfg_dvs),
+					      vaddr);
+	if (!cfg_dvs)
+		goto bad_firmware;
+
+	cfg_dvs->num_horizontal_blocks =
+			ALIGN(DIV_ROUND_UP(css->rect[IPU3_CSS_RECT_GDC].width,
+					   IMGU_DVS_BLOCK_W), 2);
+	cfg_dvs->num_vertical_blocks =
+			DIV_ROUND_UP(css->rect[IPU3_CSS_RECT_GDC].height,
+				     IMGU_DVS_BLOCK_H);
+
+	if (cfg_dvs->num_horizontal_blocks * cfg_dvs->num_vertical_blocks < 0)
+		return -EPROTO;
+
+	/* Configure TNR (temporal noise reduction) */
+
+	if (css->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) {
+		cfg_tnr = ipu3_css_fw_pipeline_params(css, cfg, m0,
+						      &cofs->dmem.tnr3,
+						      sizeof(*cfg_tnr),
+						      vaddr);
+		if (!cfg_tnr)
+			goto bad_firmware;
+
+		cfg_tnr->port_b.crop = 0;
+		cfg_tnr->port_b.elems = IMGU_ABI_ISP_DDR_WORD_BYTES;
+		cfg_tnr->port_b.width =
+				css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].width;
+		cfg_tnr->port_b.stride =
+			css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].bytesperline;
+		cfg_tnr->width_a_over_b =
+				IPU3_UAPI_ISP_VEC_ELEMS / cfg_tnr->port_b.elems;
+		cfg_tnr->frame_height =
+				css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height;
+		cfg_tnr->delay_frame = IPU3_CSS_AUX_FRAMES - 1;
+		for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+			cfg_tnr->frame_addr[i] =
+					css->aux_frames[IPU3_CSS_AUX_FRAME_TNR]
+					.mem[i].daddr;
+		for (; i < IMGU_ABI_FRAMES_TNR; i++)
+			cfg_tnr->frame_addr[i] = 0;
+	}
+
+	/* Configure ref dmem state parameters */
+
+	cfg = IMGU_ABI_PARAM_CLASS_STATE;
+	vaddr = css->binary_params_cs[cfg - 1][m0].vaddr;
+
+	cfg_ref_state = ipu3_css_fw_pipeline_params(css, cfg, m0,
+						    &sofs->dmem.ref,
+						    sizeof(*cfg_ref_state),
+						    vaddr);
+	if (!cfg_ref_state)
+		goto bad_firmware;
+
+	cfg_ref_state->ref_in_buf_idx = 0;
+	cfg_ref_state->ref_out_buf_idx = 1;
+
+	/* Configure tnr dmem state parameters */
+	if (css->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) {
+		cfg_tnr_state =
+			ipu3_css_fw_pipeline_params(css, cfg, m0,
+						    &sofs->dmem.tnr3,
+						    sizeof(*cfg_tnr_state),
+						    vaddr);
+		if (!cfg_tnr_state)
+			goto bad_firmware;
+
+		cfg_tnr_state->in_bufidx = 0;
+		cfg_tnr_state->out_bufidx = 1;
+		cfg_tnr_state->bypass_filter = 0;
+		cfg_tnr_state->total_frame_counter = 0;
+		for (i = 0; i < IMGU_ABI_BUF_SETS_TNR; i++)
+			cfg_tnr_state->buffer_frame_counter[i] = 0;
+	}
+
+	/* Configure ISP stage */
+
+	isp_stage = css->xmem_isp_stage_ptrs[pipe][stage].vaddr;
+	memset(isp_stage, 0, sizeof(*isp_stage));
+	isp_stage->blob_info = bi->blob;
+	isp_stage->binary_info = bi->info.isp.sp;
+	strcpy(isp_stage->binary_name,
+	       (char *)css->fwp + bi->blob.prog_name_offset);
+	isp_stage->mem_initializers = bi->info.isp.sp.mem_initializers;
+	for (i = IMGU_ABI_PARAM_CLASS_CONFIG; i < IMGU_ABI_PARAM_CLASS_NUM; i++)
+		for (j = 0; j < IMGU_ABI_NUM_MEMORIES; j++)
+			isp_stage->mem_initializers.params[i][j].address =
+					css->binary_params_cs[i - 1][j].daddr;
+
+	/* Configure SP stage */
+
+	sp_stage = css->xmem_sp_stage_ptrs[pipe][stage].vaddr;
+	memset(sp_stage, 0, sizeof(*sp_stage));
+
+	sp_stage->frames.in.buf_attr = buffer_sp_init;
+	for (i = 0; i < IMGU_ABI_BINARY_MAX_OUTPUT_PORTS; i++)
+		sp_stage->frames.out[i].buf_attr = buffer_sp_init;
+	sp_stage->frames.out_vf.buf_attr = buffer_sp_init;
+	sp_stage->frames.s3a_buf = buffer_sp_init;
+	sp_stage->frames.dvs_buf = buffer_sp_init;
+
+	sp_stage->stage_type = IMGU_ABI_STAGE_TYPE_ISP;
+	sp_stage->num = stage;
+	sp_stage->isp_online = 0;
+	sp_stage->isp_copy_vf = 0;
+	sp_stage->isp_copy_output = 0;
+
+	/* Enable VF output only when VF or PV queue requested by user */
+
+	sp_stage->enable.vf_output =
+				(css->vf_output_en != IPU3_NODE_VF_DISABLED);
+
+	sp_stage->frames.effective_in_res.width =
+				css->rect[IPU3_CSS_RECT_EFFECTIVE].width;
+	sp_stage->frames.effective_in_res.height =
+				css->rect[IPU3_CSS_RECT_EFFECTIVE].height;
+	sp_stage->frames.in.info.res.width =
+				css->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.width;
+	sp_stage->frames.in.info.res.height =
+				css->queue[IPU3_CSS_QUEUE_IN].fmt.mpix.height;
+	sp_stage->frames.in.info.padded_width =
+					css->queue[IPU3_CSS_QUEUE_IN].width_pad;
+	sp_stage->frames.in.info.format =
+			css->queue[IPU3_CSS_QUEUE_IN].css_fmt->frame_format;
+	sp_stage->frames.in.info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_IN].css_fmt->bit_depth;
+	sp_stage->frames.in.info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_IN].css_fmt->bayer_order;
+	sp_stage->frames.in.info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+	sp_stage->frames.in.buf_attr.buf_src.queue_id = IMGU_ABI_QUEUE_C_ID;
+	sp_stage->frames.in.buf_attr.buf_type =
+					IMGU_ABI_BUFFER_TYPE_INPUT_FRAME;
+
+	sp_stage->frames.out[0].info.res.width =
+				css->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.width;
+	sp_stage->frames.out[0].info.res.height =
+				css->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height;
+	sp_stage->frames.out[0].info.padded_width =
+				css->queue[IPU3_CSS_QUEUE_OUT].width_pad;
+	sp_stage->frames.out[0].info.format =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format;
+	sp_stage->frames.out[0].info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth;
+	sp_stage->frames.out[0].info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order;
+	sp_stage->frames.out[0].info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+	sp_stage->frames.out[0].planes.nv.uv.offset =
+				css->queue[IPU3_CSS_QUEUE_OUT].width_pad *
+				css->queue[IPU3_CSS_QUEUE_OUT].fmt.mpix.height;
+	sp_stage->frames.out[0].buf_attr.buf_src.queue_id = IMGU_ABI_QUEUE_D_ID;
+	sp_stage->frames.out[0].buf_attr.buf_type =
+					IMGU_ABI_BUFFER_TYPE_OUTPUT_FRAME;
+
+	sp_stage->frames.out[1].buf_attr.buf_src.queue_id =
+							IMGU_ABI_QUEUE_EVENT_ID;
+
+	sp_stage->frames.internal_frame_info.res.width =
+					css->rect[IPU3_CSS_RECT_BDS].width;
+	sp_stage->frames.internal_frame_info.res.height =
+					css->rect[IPU3_CSS_RECT_BDS].height;
+	sp_stage->frames.internal_frame_info.padded_width = bds_width_pad;
+
+	sp_stage->frames.internal_frame_info.format =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format;
+	sp_stage->frames.internal_frame_info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bit_depth;
+	sp_stage->frames.internal_frame_info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_OUT].css_fmt->bayer_order;
+	sp_stage->frames.internal_frame_info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+
+	sp_stage->frames.out_vf.info.res.width =
+				css->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.width;
+	sp_stage->frames.out_vf.info.res.height =
+				css->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height;
+	sp_stage->frames.out_vf.info.padded_width =
+					css->queue[IPU3_CSS_QUEUE_VF].width_pad;
+	sp_stage->frames.out_vf.info.format =
+			css->queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format;
+	sp_stage->frames.out_vf.info.raw_bit_depth =
+			css->queue[IPU3_CSS_QUEUE_VF].css_fmt->bit_depth;
+	sp_stage->frames.out_vf.info.raw_bayer_order =
+			css->queue[IPU3_CSS_QUEUE_VF].css_fmt->bayer_order;
+	sp_stage->frames.out_vf.info.raw_type = IMGU_ABI_RAW_TYPE_BAYER;
+	sp_stage->frames.out_vf.planes.yuv.u.offset =
+				css->queue[IPU3_CSS_QUEUE_VF].width_pad *
+				css->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height;
+	sp_stage->frames.out_vf.planes.yuv.v.offset =
+			css->queue[IPU3_CSS_QUEUE_VF].width_pad *
+			css->queue[IPU3_CSS_QUEUE_VF].fmt.mpix.height * 5 / 4;
+	sp_stage->frames.out_vf.buf_attr.buf_src.queue_id = IMGU_ABI_QUEUE_E_ID;
+	sp_stage->frames.out_vf.buf_attr.buf_type =
+					IMGU_ABI_BUFFER_TYPE_VF_OUTPUT_FRAME;
+
+	sp_stage->frames.s3a_buf.buf_src.queue_id = IMGU_ABI_QUEUE_F_ID;
+	sp_stage->frames.s3a_buf.buf_type = IMGU_ABI_BUFFER_TYPE_3A_STATISTICS;
+
+	sp_stage->frames.dvs_buf.buf_src.queue_id = IMGU_ABI_QUEUE_G_ID;
+	sp_stage->frames.dvs_buf.buf_type = IMGU_ABI_BUFFER_TYPE_DIS_STATISTICS;
+
+	sp_stage->dvs_envelope.width = css->rect[IPU3_CSS_RECT_ENVELOPE].width;
+	sp_stage->dvs_envelope.height =
+				css->rect[IPU3_CSS_RECT_ENVELOPE].height;
+
+	sp_stage->isp_pipe_version =
+				bi->info.isp.sp.pipeline.isp_pipe_version;
+	sp_stage->isp_deci_log_factor =
+			clamp(max(fls(css->rect[IPU3_CSS_RECT_BDS].width /
+				      IMGU_MAX_BQ_GRID_WIDTH),
+				  fls(css->rect[IPU3_CSS_RECT_BDS].height /
+				      IMGU_MAX_BQ_GRID_HEIGHT)) - 1, 3, 5);
+	sp_stage->isp_vf_downscale_bits = 0;
+	sp_stage->if_config_index = 255;
+	sp_stage->sp_enable_xnr = 0;
+	sp_stage->num_stripes = stripes;
+	sp_stage->enable.s3a = 1;
+	sp_stage->enable.dvs_stats = 1;
+
+	sp_stage->xmem_bin_addr = css->binary[css->current_binary].daddr;
+	sp_stage->xmem_map_addr = css->sp_ddr_ptrs.daddr;
+	sp_stage->isp_stage_addr = css->xmem_isp_stage_ptrs[pipe][stage].daddr;
+
+	/* Configure SP group */
+
+	sp_group = css->xmem_sp_group_ptrs.vaddr;
+	memset(sp_group, 0, sizeof(*sp_group));
+
+	sp_group->pipe[thread].num_stages = 1;
+	sp_group->pipe[thread].pipe_id = PIPE_ID;
+	sp_group->pipe[thread].thread_id = thread;
+	sp_group->pipe[thread].pipe_num = pipe;
+	sp_group->pipe[thread].num_execs = -1;
+	sp_group->pipe[thread].pipe_qos_config = -1;
+	sp_group->pipe[thread].required_bds_factor = 0;
+	sp_group->pipe[thread].dvs_frame_delay = IPU3_CSS_AUX_FRAMES - 1;
+	sp_group->pipe[thread].inout_port_config =
+					IMGU_ABI_PORT_CONFIG_TYPE_INPUT_HOST |
+					IMGU_ABI_PORT_CONFIG_TYPE_OUTPUT_HOST;
+	sp_group->pipe[thread].scaler_pp_lut = 0;
+	sp_group->pipe[thread].shading.internal_frame_origin_x_bqs_on_sctbl = 0;
+	sp_group->pipe[thread].shading.internal_frame_origin_y_bqs_on_sctbl = 0;
+	sp_group->pipe[thread].sp_stage_addr[stage] =
+				css->xmem_sp_stage_ptrs[pipe][stage].daddr;
+	sp_group->pipe[thread].pipe_config =
+			bi->info.isp.sp.enable.params ? (1 << thread) : 0;
+	sp_group->pipe[thread].pipe_config |= IMGU_ABI_PIPE_CONFIG_ACQUIRE_ISP;
+
+	/* Allocate dvs statistics metadata */
+
+	for (i = 0; i < stripes; i++)
+		if (!ipu3_dmamap_alloc(css->dev, &css->dvs_meta_data[pipe][i],
+				       sizeof(struct imgu_abi_dvs_meta_data)))
+			goto out_of_memory;
+
+	/* Initialize parameter pools */
+
+	if (ipu3_css_pool_init(css->dev, &css->pool.parameter_set_info,
+			       sizeof(struct imgu_abi_parameter_set_info)) ||
+	    ipu3_css_pool_init(css->dev, &css->pool.acc,
+			       sizeof(struct ipu3_uapi_acc_param)) ||
+	    ipu3_css_pool_init(css->dev, &css->pool.gdc,
+			       sizeof(struct ipu3_uapi_gdc_warp_param) *
+			       3 * cfg_dvs->num_horizontal_blocks / 2 *
+			       cfg_dvs->num_vertical_blocks) ||
+	    ipu3_css_pool_init(css->dev, &css->pool.obgrid,
+			       ipu3_css_fw_obgrid_size(
+			       &css->fwp->binary_header[css->current_binary])))
+		goto out_of_memory;
+
+	for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
+		if (ipu3_css_pool_init(css->dev, &css->pool.binary_params_p[i],
+				       bi->info.isp.sp.mem_initializers.params
+				       [IMGU_ABI_PARAM_CLASS_PARAM][i].size))
+			goto out_of_memory;
+
+	return 0;
+
+bad_firmware:
+	ipu3_css_pipeline_cleanup(css);
+	return -EPROTO;
+
+out_of_memory:
+	ipu3_css_pipeline_cleanup(css);
+	return -ENOMEM;
+}
+
+static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread)
+{
+	static const unsigned int sp;
+	void __iomem *const base = css->base;
+	struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]];
+	struct imgu_abi_queues __iomem *q = base + IMGU_REG_SP_DMEM_BASE(sp) +
+	    bi->info.sp.host_sp_queue;
+
+	return queue >= 0 ? readb(&q->host2sp_bufq_info[thread][queue].end) :
+	    readb(&q->host2sp_evtq_info.end);
+}
+
+/* Sent data to sp using given buffer queue, or if queue < 0, event queue. */
+static int ipu3_css_queue_data(struct ipu3_css *css,
+			       int queue, int thread, u32 data)
+{
+	static const unsigned int sp;
+	void __iomem *const base = css->base;
+	struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]];
+	struct imgu_abi_queues __iomem *q = base + IMGU_REG_SP_DMEM_BASE(sp) +
+	    bi->info.sp.host_sp_queue;
+	u8 size, start, end, end2;
+
+	if (queue >= 0) {
+		size = readb(&q->host2sp_bufq_info[thread][queue].size);
+		start = readb(&q->host2sp_bufq_info[thread][queue].start);
+		end = readb(&q->host2sp_bufq_info[thread][queue].end);
+	} else {
+		size = readb(&q->host2sp_evtq_info.size);
+		start = readb(&q->host2sp_evtq_info.start);
+		end = readb(&q->host2sp_evtq_info.end);
+	}
+
+	if (size == 0)
+		return -EIO;
+
+	end2 = (end + 1) % size;
+	if (end2 == start)
+		return -EBUSY;	/* Queue full */
+
+	if (queue >= 0) {
+		writel(data, &q->host2sp_bufq[thread][queue][end]);
+		writeb(end2, &q->host2sp_bufq_info[thread][queue].end);
+	} else {
+		writel(data, &q->host2sp_evtq[end]);
+		writeb(end2, &q->host2sp_evtq_info.end);
+	}
+
+	return 0;
+}
+
+/* Receive data using given buffer queue, or if queue < 0, event queue. */
+static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data)
+{
+	static const unsigned int sp;
+	void __iomem *const base = css->base;
+	struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]];
+	struct imgu_abi_queues __iomem *q = base + IMGU_REG_SP_DMEM_BASE(sp) +
+	    bi->info.sp.host_sp_queue;
+	u8 size, start, end, start2;
+
+	if (queue >= 0) {
+		size = readb(&q->sp2host_bufq_info[queue].size);
+		start = readb(&q->sp2host_bufq_info[queue].start);
+		end = readb(&q->sp2host_bufq_info[queue].end);
+	} else {
+		size = readb(&q->sp2host_evtq_info.size);
+		start = readb(&q->sp2host_evtq_info.start);
+		end = readb(&q->sp2host_evtq_info.end);
+	}
+
+	if (size == 0)
+		return -EIO;
+
+	if (end == start)
+		return -EBUSY;	/* Queue empty */
+
+	start2 = (start + 1) % size;
+
+	if (queue >= 0) {
+		*data = readl(&q->sp2host_bufq[queue][start]);
+		writeb(start2, &q->sp2host_bufq_info[queue].start);
+	} else {
+		int r;
+
+		*data = readl(&q->sp2host_evtq[start]);
+		writeb(start2, &q->sp2host_evtq_info.start);
+
+		/* Acknowledge events dequeued from event queue */
+		r = ipu3_css_queue_data(css, queue, 0,
+					IMGU_ABI_EVENT_EVENT_DEQUEUED);
+		if (r < 0)
+			return r;
+	}
+
+	return 0;
+}
+
+/* Free binary-specific resources */
+static void ipu3_css_binary_cleanup(struct ipu3_css *css)
+{
+	unsigned int i, j;
+
+	for (j = 0; j < IMGU_ABI_PARAM_CLASS_NUM - 1; j++)
+		for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
+			ipu3_dmamap_free(css->dev,
+					 &css->binary_params_cs[j][i]);
+
+	j = IPU3_CSS_AUX_FRAME_REF;
+	for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+		ipu3_dmamap_free(css->dev, &css->aux_frames[j].mem[i]);
+
+	j = IPU3_CSS_AUX_FRAME_TNR;
+	for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+		ipu3_dmamap_free(css->dev, &css->aux_frames[j].mem[i]);
+}
+
+/* allocate binary-specific resources */
+static int ipu3_css_binary_setup(struct ipu3_css *css, bool pre_allocate)
+{
+	struct imgu_fw_info *bi = &css->fwp->binary_header[css->current_binary];
+	static const int BYPC = 2;	/* Bytes per component */
+	unsigned int w, h, i, j, size;
+
+	if (pre_allocate) {
+		for (j = IMGU_ABI_PARAM_CLASS_CONFIG;
+		     j < IMGU_ABI_PARAM_CLASS_NUM; j++)
+			for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) {
+				if (!ipu3_dmamap_alloc(css->dev,
+					&css->binary_params_cs[j - 1][i],
+					CSS_ABI_SIZE))
+					goto out_of_memory;
+			}
+
+		for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+			if (!ipu3_dmamap_alloc(css->dev,
+				&css->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i],
+				CSS_BDS_SIZE))
+				goto out_of_memory;
+
+		for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+			if (!ipu3_dmamap_alloc(css->dev,
+				&css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i],
+				CSS_GDC_SIZE))
+				goto out_of_memory;
+
+		return 0;
+	}
+
+	for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++)
+		for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) {
+			if (ipu3_css_dma_buffer_resize(css->dev,
+				&css->binary_params_cs[j - 1][i],
+				bi->info.isp.sp.mem_initializers.params[j][i].size))
+				goto out_of_memory;
+		}
+
+	/* Allocate internal frame buffers */
+
+	/* Reference frames for DVS, FRAME_FORMAT_YUV420_16 */
+	css->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel = BYPC;
+	css->aux_frames[IPU3_CSS_AUX_FRAME_REF].width =
+					css->rect[IPU3_CSS_RECT_BDS].width;
+	css->aux_frames[IPU3_CSS_AUX_FRAME_REF].height =
+				ALIGN(css->rect[IPU3_CSS_RECT_BDS].height,
+				      IMGU_DVS_BLOCK_H) + 2 * IMGU_GDC_BUF_Y;
+	h = css->aux_frames[IPU3_CSS_AUX_FRAME_REF].height;
+	w = ALIGN(css->rect[IPU3_CSS_RECT_BDS].width,
+		  2 * IPU3_UAPI_ISP_VEC_ELEMS) + 2 * IMGU_GDC_BUF_X;
+	css->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperline =
+		css->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel * w;
+	size = w * h * BYPC + (w / 2) * (h / 2) * BYPC * 2;
+	for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+		if (ipu3_css_dma_buffer_resize(
+				css->dev,
+				&css->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i],
+				size))
+			goto out_of_memory;
+
+	/* TNR frames for temporal noise reduction, FRAME_FORMAT_YUV_LINE */
+	css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].bytesperpixel = 1;
+	css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].width =
+			roundup(css->rect[IPU3_CSS_RECT_GDC].width,
+				bi->info.isp.sp.block.block_width *
+				IPU3_UAPI_ISP_VEC_ELEMS);
+	css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height =
+			roundup(css->rect[IPU3_CSS_RECT_GDC].height,
+				bi->info.isp.sp.block.output_block_height);
+
+	w = css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].width;
+	css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].bytesperline = w;
+	h = css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height;
+	size = w * ALIGN(h * 3 / 2 + 3, 2);	/* +3 for vf_pp prefetch */
+
+	for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
+		if (ipu3_css_dma_buffer_resize(
+			css->dev,
+			&css->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i], size))
+			goto out_of_memory;
+
+	return 0;
+
+out_of_memory:
+	ipu3_css_binary_cleanup(css);
+	return -ENOMEM;
+}
+
+int ipu3_css_start_streaming(struct ipu3_css *css)
+{
+	u32 data;
+	int r;
+
+	if (css->streaming)
+		return -EPROTO;
+
+	r = ipu3_css_binary_setup(css, false);
+	if (r < 0)
+		return r;
+
+	r = ipu3_css_hw_init(css);
+	if (r < 0)
+		return r;
+
+	r = ipu3_css_hw_start(css);
+	if (r < 0)
+		goto fail;
+
+	r = ipu3_css_pipeline_init(css);
+	if (r < 0)
+		goto fail;
+
+	css->streaming = true;
+	css->frame = 0;
+
+	/* Initialize parameters to default */
+	r = ipu3_css_set_parameters(css, NULL, NULL, 0, NULL, 0);
+	if (r < 0)
+		goto fail;
+
+	while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data)))
+		;
+	if (r != -EBUSY)
+		goto fail;
+
+	while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data)))
+		;
+	if (r != -EBUSY)
+		goto fail;
+
+	r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, 0,
+				IMGU_ABI_EVENT_START_STREAM);
+	if (r < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	css->streaming = false;
+	ipu3_css_hw_cleanup(css);
+	ipu3_css_pipeline_cleanup(css);
+	ipu3_css_binary_cleanup(css);
+
+	return r;
+}
+
+void ipu3_css_stop_streaming(struct ipu3_css *css)
+{
+	struct ipu3_css_buffer *b, *b0;
+	unsigned int i;
+	int r;
+
+	r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, 0,
+				IMGU_ABI_EVENT_STOP_STREAM);
+	if (r < 0)
+		dev_warn(css->dev, "failed on stop stream event\n");
+
+	if (!css->streaming)
+		return;
+
+	ipu3_css_hw_cleanup(css);
+
+	ipu3_css_pipeline_cleanup(css);
+
+	for (i = 0; i < IPU3_CSS_QUEUES; i++)
+		list_for_each_entry_safe(b, b0, &css->queue[i].bufs, list) {
+			b->state = IPU3_CSS_BUFFER_FAILED;
+			list_del(&b->list);
+		}
+
+	css->streaming = false;
+}
+
+bool ipu3_css_queue_empty(struct ipu3_css *css)
+{
+	unsigned int i;
+
+	for (i = 0; i < IPU3_CSS_QUEUES; i++)
+		if (!list_empty(&css->queue[i].bufs))
+			return false;
+	return true;
+}
+
+bool ipu3_css_is_streaming(struct ipu3_css *css)
+{
+	return css->streaming;
+}
+
+void ipu3_css_cleanup(struct ipu3_css *css)
+{
+	unsigned int i, j;
+
+	ipu3_css_stop_streaming(css);
+	ipu3_css_binary_cleanup(css);
+
+	for (i = 0; i < IPU3_CSS_QUEUES; i++)
+		for (j = 0; j < ARRAY_SIZE(css->abi_buffers[i]); j++)
+			ipu3_dmamap_free(css->dev, &css->abi_buffers[i][j]);
+
+	for (i = 0; i < IPU3_CSS_PIPE_ID_NUM; i++)
+		for (j = 0; j < IMGU_ABI_MAX_STAGES; j++) {
+			ipu3_dmamap_free(css->dev,
+					 &css->xmem_sp_stage_ptrs[i][j]);
+			ipu3_dmamap_free(css->dev,
+					 &css->xmem_isp_stage_ptrs[i][j]);
+		}
+
+	ipu3_dmamap_free(css->dev, &css->sp_ddr_ptrs);
+	ipu3_dmamap_free(css->dev, &css->xmem_sp_group_ptrs);
+
+	ipu3_css_fw_cleanup(css);
+}
+
+int ipu3_css_init(struct device *dev, struct ipu3_css *css,
+		  void __iomem *base, int length)
+{
+	unsigned int i, j;
+	int r;
+
+	/* Initialize main data structure */
+	css->dev = dev;
+	css->base = base;
+	css->iomem_length = length;
+	css->current_binary = IPU3_CSS_DEFAULT_BINARY;
+	css->pipe_id = IPU3_CSS_PIPE_ID_NUM;
+
+	for (i = 0; i < IPU3_CSS_QUEUES; i++) {
+		r = ipu3_css_queue_init(&css->queue[i], NULL, 0);
+		if (r)
+			return r;
+	}
+
+	r = ipu3_css_fw_init(css);
+	if (r)
+		return r;
+
+	/* Allocate and map common structures with imgu hardware */
+
+	for (i = 0; i < IPU3_CSS_PIPE_ID_NUM; i++)
+		for (j = 0; j < IMGU_ABI_MAX_STAGES; j++) {
+			if (!ipu3_dmamap_alloc(dev,
+					&css->xmem_sp_stage_ptrs[i][j],
+					sizeof(struct imgu_abi_sp_stage)))
+				goto error_no_memory;
+			if (!ipu3_dmamap_alloc(dev,
+					&css->xmem_isp_stage_ptrs[i][j],
+					sizeof(struct imgu_abi_isp_stage)))
+				goto error_no_memory;
+		}
+
+	if (!ipu3_dmamap_alloc(dev, &css->sp_ddr_ptrs,
+			       ALIGN(sizeof(struct imgu_abi_ddr_address_map),
+				     IMGU_ABI_ISP_DDR_WORD_BYTES)))
+		goto error_no_memory;
+
+	if (!ipu3_dmamap_alloc(dev, &css->xmem_sp_group_ptrs,
+			       sizeof(struct imgu_abi_sp_group)))
+		goto error_no_memory;
+
+	for (i = 0; i < IPU3_CSS_QUEUES; i++)
+		for (j = 0; j < ARRAY_SIZE(css->abi_buffers[i]); j++)
+			if (!ipu3_dmamap_alloc(dev, &css->abi_buffers[i][j],
+					       sizeof(struct imgu_abi_buffer)))
+				goto error_no_memory;
+
+	if (ipu3_css_binary_setup(css, true))
+		goto error_binary_setup;
+
+	return 0;
+
+error_binary_setup:
+	ipu3_css_binary_cleanup(css);
+error_no_memory:
+	ipu3_css_cleanup(css);
+
+	return -ENOMEM;
+}
+
+static u32 ipu3_css_adjust(u32 res, u32 align)
+{
+	if (res < IPU3_CSS_MIN_RES)
+		res = IPU3_CSS_MIN_RES;
+	res = roundclosest(res, align);
+
+	return res;
+}
+
+/* Select a binary matching the required resolutions and formats */
+static int ipu3_css_find_binary(struct ipu3_css *css,
+				struct ipu3_css_queue queue[IPU3_CSS_QUEUES],
+				struct v4l2_rect rects[IPU3_CSS_RECTS])
+{
+	const int binary_nr = css->fwp->file_header.binary_nr;
+	unsigned int binary_mode = (css->pipe_id == IPU3_CSS_PIPE_ID_CAPTURE) ?
+		IA_CSS_BINARY_MODE_PRIMARY : IA_CSS_BINARY_MODE_VIDEO;
+	const char *name;
+
+	const struct v4l2_pix_format_mplane *in =
+					&queue[IPU3_CSS_QUEUE_IN].fmt.mpix;
+	const struct v4l2_pix_format_mplane *out =
+					&queue[IPU3_CSS_QUEUE_OUT].fmt.mpix;
+	const struct v4l2_pix_format_mplane *vf =
+					&queue[IPU3_CSS_QUEUE_VF].fmt.mpix;
+
+	int i, j;
+	u32 stripe_w = 0;
+	u32 stripe_h = 0;
+
+	if (!ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN]))
+		return -EINVAL;
+
+	/* Find out the strip size boundary */
+	for (i = 0; i < binary_nr; i++) {
+		struct imgu_fw_info *bi = &css->fwp->binary_header[i];
+
+		u32 max_width = bi->info.isp.sp.output.max_width;
+		u32 max_height = bi->info.isp.sp.output.max_height;
+
+		if (bi->info.isp.sp.iterator.num_stripes <= 1) {
+			stripe_w = stripe_w ?
+				min(stripe_w, max_width) : max_width;
+			stripe_h = stripe_h ?
+				min(stripe_h, max_height) : max_height;
+		}
+	}
+
+	for (i = 0; i < binary_nr; i++) {
+		struct imgu_fw_info *bi = &css->fwp->binary_header[i];
+		enum imgu_abi_frame_format q_fmt;
+
+		name = (void *)css->fwp + bi->blob.prog_name_offset;
+
+		/* Check that binary supports memory-to-memory processing */
+		if (bi->info.isp.sp.input.source !=
+		    IMGU_ABI_BINARY_INPUT_SOURCE_MEMORY)
+			continue;
+
+		/* Check that binary supports raw10 input */
+		if (!bi->info.isp.sp.enable.input_feeder &&
+		    !bi->info.isp.sp.enable.input_raw)
+			continue;
+
+		/* Check binary mode */
+		if (bi->info.isp.sp.pipeline.mode != binary_mode)
+			continue;
+
+		/* Since input is RGGB bayer, need to process colors */
+		if (bi->info.isp.sp.enable.luma_only)
+			continue;
+
+		if (in->width < bi->info.isp.sp.input.min_width ||
+		    in->width > bi->info.isp.sp.input.max_width ||
+		    in->height < bi->info.isp.sp.input.min_height ||
+		    in->height > bi->info.isp.sp.input.max_height)
+			continue;
+
+		if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) {
+			if (bi->info.isp.num_output_pins <= 0)
+				continue;
+
+			q_fmt = queue[IPU3_CSS_QUEUE_OUT].css_fmt->frame_format;
+			for (j = 0; j < bi->info.isp.num_output_formats; j++)
+				if (bi->info.isp.output_formats[j] == q_fmt)
+					break;
+			if (j >= bi->info.isp.num_output_formats)
+				continue;
+
+			if (out->width < bi->info.isp.sp.output.min_width ||
+			    out->width > bi->info.isp.sp.output.max_width ||
+			    out->height < bi->info.isp.sp.output.min_height ||
+			    out->height > bi->info.isp.sp.output.max_height)
+				continue;
+
+			if (out->width > bi->info.isp.sp.internal.max_width ||
+			    out->height > bi->info.isp.sp.internal.max_height)
+				continue;
+		}
+
+		if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) {
+			if (bi->info.isp.num_output_pins <= 1)
+				continue;
+
+			q_fmt = queue[IPU3_CSS_QUEUE_VF].css_fmt->frame_format;
+			for (j = 0; j < bi->info.isp.num_output_formats; j++)
+				if (bi->info.isp.output_formats[j] == q_fmt)
+					break;
+			if (j >= bi->info.isp.num_output_formats)
+				continue;
+
+			if (vf->width < bi->info.isp.sp.output.min_width ||
+			    vf->width > bi->info.isp.sp.output.max_width ||
+			    vf->height < bi->info.isp.sp.output.min_height ||
+			    vf->height > bi->info.isp.sp.output.max_height)
+				continue;
+		}
+
+		/* All checks passed, select the binary */
+		dev_dbg(css->dev, "using binary %s\n", name);
+		return i;
+	}
+
+	/* Can not find suitable binary for these parameters */
+	return -EINVAL;
+}
+
+/*
+ * Check that there is a binary matching requirements. Parameters may be
+ * NULL indicating disabled input/output. Return negative if given
+ * parameters can not be supported or on error, zero or positive indicating
+ * found binary number. May modify the given parameters if not exact match
+ * is found.
+ */
+int ipu3_css_fmt_try(struct ipu3_css *css,
+		     struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES],
+		     struct v4l2_rect *rects[IPU3_CSS_RECTS])
+{
+	static const u32 EFF_ALIGN_W = 2;
+	static const u32 BDS_ALIGN_W = 4;
+	static const u32 OUT_ALIGN_W = 8;
+	static const u32 OUT_ALIGN_H = 4;
+	static const u32 VF_ALIGN_W  = 2;
+	static const char *qnames[IPU3_CSS_QUEUES] = {
+		[IPU3_CSS_QUEUE_IN] = "in",
+		[IPU3_CSS_QUEUE_PARAMS]    = "params",
+		[IPU3_CSS_QUEUE_OUT] = "out",
+		[IPU3_CSS_QUEUE_VF] = "vf",
+		[IPU3_CSS_QUEUE_STAT_3A]   = "3a",
+		[IPU3_CSS_QUEUE_STAT_DVS]  = "dvs",
+	};
+	static const char *rnames[IPU3_CSS_RECTS] = {
+		[IPU3_CSS_RECT_EFFECTIVE] = "effective resolution",
+		[IPU3_CSS_RECT_BDS]       = "bayer-domain scaled resolution",
+		[IPU3_CSS_RECT_ENVELOPE]  = "DVS envelope size",
+		[IPU3_CSS_RECT_GDC]  = "GDC output res",
+	};
+	struct v4l2_rect r[IPU3_CSS_RECTS] = { };
+	struct v4l2_rect *const eff = &r[IPU3_CSS_RECT_EFFECTIVE];
+	struct v4l2_rect *const bds = &r[IPU3_CSS_RECT_BDS];
+	struct v4l2_rect *const env = &r[IPU3_CSS_RECT_ENVELOPE];
+	struct v4l2_rect *const gdc = &r[IPU3_CSS_RECT_GDC];
+	struct ipu3_css_queue q[IPU3_CSS_QUEUES];
+	struct v4l2_pix_format_mplane *const in =
+					&q[IPU3_CSS_QUEUE_IN].fmt.mpix;
+	struct v4l2_pix_format_mplane *const out =
+					&q[IPU3_CSS_QUEUE_OUT].fmt.mpix;
+	struct v4l2_pix_format_mplane *const vf =
+					&q[IPU3_CSS_QUEUE_VF].fmt.mpix;
+	int binary, i, size;
+
+	/* Decide which pipe to use */
+	if (css->vf_output_en == IPU3_NODE_PV_ENABLED)
+		css->pipe_id = IPU3_CSS_PIPE_ID_CAPTURE;
+	else if (css->vf_output_en == IPU3_NODE_VF_ENABLED)
+		css->pipe_id = IPU3_CSS_PIPE_ID_VIDEO;
+
+	/* Adjust all formats, get statistics buffer sizes and formats */
+	for (i = 0; i < IPU3_CSS_QUEUES; i++) {
+		if (fmts[i])
+			dev_dbg(css->dev, "%s %s: (%i,%i) fmt 0x%x\n", __func__,
+				qnames[i], fmts[i]->width, fmts[i]->height,
+				fmts[i]->pixelformat);
+		else
+			dev_dbg(css->dev, "%s %s: (not set)\n", __func__,
+				qnames[i]);
+		if (ipu3_css_queue_init(&q[i], fmts[i],
+					IPU3_CSS_QUEUE_TO_FLAGS(i))) {
+			dev_notice(css->dev, "can not initialize queue %s\n",
+				   qnames[i]);
+			return -EINVAL;
+		}
+	}
+	for (i = 0; i < IPU3_CSS_RECTS; i++) {
+		if (rects[i]) {
+			dev_dbg(css->dev, "%s %s: (%i,%i)\n", __func__,
+				rnames[i], rects[i]->width, rects[i]->height);
+			r[i].width  = rects[i]->width;
+			r[i].height = rects[i]->height;
+		} else {
+			dev_dbg(css->dev, "%s %s: (not set)\n", __func__,
+				rnames[i]);
+		}
+		/* For now, force known good resolutions */
+		r[i].left = 0;
+		r[i].top  = 0;
+	}
+
+	/* Always require one input and vf only if out is also enabled */
+	if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) ||
+	    (ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_VF]) &&
+	    !ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT]))) {
+		dev_dbg(css->dev, "required queues are disabled\n");
+		return -EINVAL;
+	}
+
+	if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) {
+		out->width = in->width;
+		out->height = in->height;
+	}
+	if (eff->width <= 0 || eff->height <= 0) {
+		eff->width = in->width;
+		eff->height = in->height;
+	}
+	if (bds->width <= 0 || bds->height <= 0) {
+		bds->width = out->width;
+		bds->height = out->height;
+	}
+	if (gdc->width <= 0 || gdc->height <= 0) {
+		gdc->width = out->width;
+		gdc->height = out->height;
+	}
+
+	in->width   = ipu3_css_adjust(in->width, 1);
+	in->height  = ipu3_css_adjust(in->height, 1);
+	eff->width  = ipu3_css_adjust(eff->width, EFF_ALIGN_W);
+	eff->height = ipu3_css_adjust(eff->height, 1);
+	bds->width  = ipu3_css_adjust(bds->width, BDS_ALIGN_W);
+	bds->height = ipu3_css_adjust(bds->height, 1);
+	gdc->width  = ipu3_css_adjust(gdc->width, OUT_ALIGN_W);
+	gdc->height = ipu3_css_adjust(gdc->height, OUT_ALIGN_H);
+	out->width  = ipu3_css_adjust(out->width, OUT_ALIGN_W);
+	out->height = ipu3_css_adjust(out->height, OUT_ALIGN_H);
+	vf->width   = ipu3_css_adjust(vf->width, VF_ALIGN_W);
+	vf->height  = ipu3_css_adjust(vf->height, 1);
+
+	size = (bds->width - gdc->width) / 2 - FILTER_SIZE;
+	env->width = size < MIN_ENVELOPE ? MIN_ENVELOPE : size;
+	size = (bds->height - gdc->height) / 2 - FILTER_SIZE;
+	env->height = size < MIN_ENVELOPE ? MIN_ENVELOPE : size;
+
+	binary = ipu3_css_find_binary(css, q, r);
+	if (binary < 0) {
+		dev_err(css->dev, "failed to find suitable binary\n");
+		return -EINVAL;
+	}
+
+	/* Final adjustment and set back the queried formats */
+	for (i = 0; i < IPU3_CSS_QUEUES; i++) {
+		if (fmts[i]) {
+			if (ipu3_css_queue_init(&q[i], &q[i].fmt.mpix,
+						IPU3_CSS_QUEUE_TO_FLAGS(i))) {
+				dev_err(css->dev,
+					"final resolution adjustment failed\n");
+				return -EINVAL;
+			}
+			*fmts[i] = q[i].fmt.mpix;
+		}
+	}
+
+	for (i = 0; i < IPU3_CSS_RECTS; i++)
+		if (rects[i])
+			*rects[i] = r[i];
+
+	dev_dbg(css->dev,
+		"in(%u,%u) if(%u,%u) ds(%u,%u) gdc(%u,%u) out(%u,%u) vf(%u,%u)",
+		 in->width, in->height, eff->width, eff->height,
+		 bds->width, bds->height, gdc->width, gdc->height,
+		 out->width, out->height, vf->width, vf->height);
+
+	return binary;
+}
+
+int ipu3_css_fmt_set(struct ipu3_css *css,
+		     struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES],
+		     struct v4l2_rect *rects[IPU3_CSS_RECTS])
+{
+	struct v4l2_rect rect_data[IPU3_CSS_RECTS];
+	struct v4l2_rect *all_rects[IPU3_CSS_RECTS];
+	unsigned int i;
+	int r;
+
+	for (i = 0; i < IPU3_CSS_RECTS; i++) {
+		if (rects[i])
+			rect_data[i] = *rects[i];
+		else
+			memset(&rect_data[i], 0, sizeof(rect_data[i]));
+		all_rects[i] = &rect_data[i];
+	}
+	r = ipu3_css_fmt_try(css, fmts, all_rects);
+	if (r < 0)
+		return r;
+	css->current_binary = (unsigned int)r;
+
+	for (i = 0; i < IPU3_CSS_QUEUES; i++)
+		if (ipu3_css_queue_init(&css->queue[i], fmts[i],
+					IPU3_CSS_QUEUE_TO_FLAGS(i)))
+			return -EINVAL;
+	for (i = 0; i < IPU3_CSS_RECTS; i++) {
+		css->rect[i] = rect_data[i];
+		if (rects[i])
+			*rects[i] = rect_data[i];
+	}
+
+	return 0;
+}
+
+int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt)
+{
+	switch (fmt->dataformat) {
+	case V4L2_META_FMT_IPU3_PARAMS:
+		fmt->buffersize = sizeof(struct ipu3_uapi_params);
+		break;
+	case V4L2_META_FMT_IPU3_STAT_3A:
+		fmt->buffersize = sizeof(struct ipu3_uapi_stats_3a);
+		break;
+	case V4L2_META_FMT_IPU3_STAT_DVS:
+		fmt->buffersize = sizeof(struct ipu3_uapi_stats_dvs);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Queue given buffer to CSS. ipu3_css_buf_prepare() must have been first
+ * called for the buffer. May be called from interrupt context.
+ * Returns 0 on success, -EBUSY if the buffer queue is full, or some other
+ * code on error conditions.
+ */
+int ipu3_css_buf_queue(struct ipu3_css *css, struct ipu3_css_buffer *b)
+{
+	static const int thread;
+	struct imgu_abi_buffer *abi_buf;
+	struct imgu_addr_t *buf_addr;
+	u32 data;
+	int r;
+
+	if (!css->streaming)
+		return -EPROTO;	/* CSS or buffer in wrong state */
+
+	if (b->queue >= IPU3_CSS_QUEUES || !ipu3_css_queues[b->queue].qid)
+		return -EINVAL;
+
+	b->queue_pos = ipu3_css_queue_pos(css, ipu3_css_queues[b->queue].qid,
+					  thread);
+
+	if (b->queue_pos >= ARRAY_SIZE(css->abi_buffers[b->queue]))
+		return -EIO;
+	abi_buf = css->abi_buffers[b->queue][b->queue_pos].vaddr;
+
+	/* Fill struct abi_buffer for firmware */
+	memset(abi_buf, 0, sizeof(*abi_buf));
+
+	buf_addr = (void *)abi_buf + ipu3_css_queues[b->queue].ptr_ofs;
+	*(imgu_addr_t *)buf_addr = b->daddr;
+
+	if (b->queue == IPU3_CSS_QUEUE_STAT_3A)
+		abi_buf->payload.s3a.data.dmem.s3a_tbl = b->daddr;
+
+	if (b->queue == IPU3_CSS_QUEUE_OUT)
+		abi_buf->payload.frame.padded_width =
+				css->queue[IPU3_CSS_QUEUE_OUT].width_pad;
+
+	if (b->queue == IPU3_CSS_QUEUE_VF)
+		abi_buf->payload.frame.padded_width =
+					css->queue[IPU3_CSS_QUEUE_VF].width_pad;
+
+	list_add_tail(&b->list, &css->queue[b->queue].bufs);
+	b->state = IPU3_CSS_BUFFER_QUEUED;
+
+	data = css->abi_buffers[b->queue][b->queue_pos].daddr;
+	r = ipu3_css_queue_data(css, ipu3_css_queues[b->queue].qid,
+				thread, data);
+	if (r < 0)
+		goto queueing_failed;
+
+	data = IMGU_ABI_EVENT_BUFFER_ENQUEUED(thread,
+					      ipu3_css_queues[b->queue].qid);
+	r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, 0, data);
+	if (r < 0)
+		goto queueing_failed;
+
+	dev_dbg(css->dev, "queued buffer %p to css queue %i\n", b, b->queue);
+
+	return 0;
+
+queueing_failed:
+	b->state = (r == -EBUSY || r == -EAGAIN) ?
+		IPU3_CSS_BUFFER_NEW : IPU3_CSS_BUFFER_FAILED;
+	list_del(&b->list);
+
+	return r;
+}
+
+/*
+ * Get next ready CSS buffer. Returns -EAGAIN in which case the function
+ * should be called again, or -EBUSY which means that there are no more
+ * buffers available. May be called from interrupt context.
+ */
+struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
+{
+	static const int thread;
+	static const unsigned char evtype_to_queue[] = {
+		[IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE] = IPU3_CSS_QUEUE_IN,
+		[IMGU_ABI_EVTTYPE_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_OUT,
+		[IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_VF,
+		[IMGU_ABI_EVTTYPE_3A_STATS_DONE] = IPU3_CSS_QUEUE_STAT_3A,
+		[IMGU_ABI_EVTTYPE_DIS_STATS_DONE] = IPU3_CSS_QUEUE_STAT_DVS,
+	};
+	struct ipu3_css_buffer *b = ERR_PTR(-EAGAIN);
+	u32 event, daddr;
+	int evtype, pipe, pipeid, queue, qid, r;
+
+	if (!css->streaming)
+		return ERR_PTR(-EPROTO);
+
+	r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event);
+	if (r < 0)
+		return ERR_PTR(r);
+
+	evtype = (event & IMGU_ABI_EVTTYPE_EVENT_MASK) >>
+		  IMGU_ABI_EVTTYPE_EVENT_SHIFT;
+
+	switch (evtype) {
+	case IMGU_ABI_EVTTYPE_OUT_FRAME_DONE:
+	case IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE:
+	case IMGU_ABI_EVTTYPE_3A_STATS_DONE:
+	case IMGU_ABI_EVTTYPE_DIS_STATS_DONE:
+	case IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE:
+		pipe = (event & IMGU_ABI_EVTTYPE_PIPE_MASK) >>
+			IMGU_ABI_EVTTYPE_PIPE_SHIFT;
+		pipeid = (event & IMGU_ABI_EVTTYPE_PIPEID_MASK) >>
+			IMGU_ABI_EVTTYPE_PIPEID_SHIFT;
+		queue = evtype_to_queue[evtype];
+		qid = ipu3_css_queues[queue].qid;
+
+		if (qid >= IMGU_ABI_QUEUE_NUM) {
+			dev_err(css->dev, "Invalid qid: %i\n", qid);
+			return ERR_PTR(-EIO);
+		}
+
+		dev_dbg(css->dev,
+			"event: buffer done 0x%x queue %i pipe %i pipeid %i\n",
+			event, queue, pipe, pipeid);
+
+		r = ipu3_css_dequeue_data(css, qid, &daddr);
+		if (r < 0) {
+			dev_err(css->dev, "failed to dequeue buffer\n");
+			/* Force real error, not -EBUSY */
+			return ERR_PTR(-EIO);
+		}
+
+		r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, thread,
+					IMGU_ABI_EVENT_BUFFER_DEQUEUED(qid));
+		if (r < 0) {
+			dev_err(css->dev, "failed to queue event\n");
+			return ERR_PTR(-EIO);
+		}
+
+		if (list_empty(&css->queue[queue].bufs)) {
+			dev_err(css->dev, "event on empty queue\n");
+			return ERR_PTR(-EIO);
+		}
+		b = list_first_entry(&css->queue[queue].bufs,
+				     struct ipu3_css_buffer, list);
+		if (queue != b->queue ||
+		    daddr != css->abi_buffers[b->queue][b->queue_pos].daddr) {
+			dev_err(css->dev, "dequeued bad buffer 0x%x\n", daddr);
+			return ERR_PTR(-EIO);
+		}
+		b->state = IPU3_CSS_BUFFER_DONE;
+		list_del(&b->list);
+		break;
+	case IMGU_ABI_EVTTYPE_PIPELINE_DONE:
+		dev_dbg(css->dev, "event: pipeline done 0x%x for frame %ld\n",
+			event, css->frame);
+
+		if (css->frame == LONG_MAX)
+			css->frame = 0;
+		else
+			css->frame++;
+		break;
+	case IMGU_ABI_EVTTYPE_TIMER:
+		r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event);
+		if (r < 0)
+			return ERR_PTR(r);
+
+		if ((event & IMGU_ABI_EVTTYPE_EVENT_MASK) >>
+		    IMGU_ABI_EVTTYPE_EVENT_SHIFT == IMGU_ABI_EVTTYPE_TIMER)
+			dev_dbg(css->dev, "event: timer\n");
+		else
+			dev_warn(css->dev, "half of timer event missing\n");
+		break;
+	case IMGU_ABI_EVTTYPE_FW_WARNING:
+		dev_warn(css->dev, "event: firmware warning 0x%x\n", event);
+		break;
+	case IMGU_ABI_EVTTYPE_FW_ASSERT:
+		dev_err(css->dev,
+			"event: firmware assert 0x%x module_id %i line_no %i\n",
+			event,
+			(event & IMGU_ABI_EVTTYPE_MODULEID_MASK) >>
+			IMGU_ABI_EVTTYPE_MODULEID_SHIFT,
+			swab16((event & IMGU_ABI_EVTTYPE_LINENO_MASK) >>
+			       IMGU_ABI_EVTTYPE_LINENO_SHIFT));
+		break;
+	default:
+		dev_warn(css->dev, "received unknown event 0x%x\n", event);
+	}
+
+	return b;
+}
+
+/*
+ * Get a new set of parameters from pool and initialize them based on
+ * the parameters params, gdc, and obgrid. Any of these may be NULL,
+ * in which case the previously set parameters are used.
+ * If parameters haven't been set previously, initialize from scratch.
+ *
+ * Return index to css->parameter_set_info which has the newly created
+ * parameters or negative value on error.
+ */
+int ipu3_css_set_parameters(struct ipu3_css *css,
+			    struct ipu3_uapi_params *set_params,
+			    struct ipu3_uapi_gdc_warp_param *set_gdc,
+			    unsigned int gdc_bytes,
+			    struct ipu3_uapi_obgrid_param *set_obgrid,
+			    unsigned int obgrid_bytes)
+{
+	static const unsigned int queue_id = IMGU_ABI_QUEUE_A_ID;
+	const int stage = 0, thread = 0;
+	const struct imgu_fw_info *bi;
+	unsigned int obgrid_size, stripes, i;
+	struct ipu3_uapi_flags *use = set_params ? &set_params->use : NULL;
+
+	/* Destination buffers which are filled here */
+	struct imgu_abi_parameter_set_info *param_set;
+	struct ipu3_uapi_acc_param *acc = NULL;
+	struct ipu3_uapi_gdc_warp_param *gdc = NULL;
+	struct ipu3_uapi_obgrid_param *obgrid = NULL;
+	const struct ipu3_css_map *map;
+	void *vmem0 = NULL;
+	void *dmem0 = NULL;
+
+	enum imgu_abi_memories m;
+	int r = -EBUSY;
+
+	if (!css->streaming)
+		return -EPROTO;
+
+	bi = &css->fwp->binary_header[css->current_binary];
+	obgrid_size = ipu3_css_fw_obgrid_size(bi);
+	stripes = bi->info.isp.sp.iterator.num_stripes ? : 1;
+
+	/*
+	 * Check that we can get a new parameter_set_info from the pool.
+	 * If this succeeds, then all of the other pool_get() calls below
+	 * should also succeed.
+	 */
+	if (ipu3_css_pool_get(&css->pool.parameter_set_info, css->frame) < 0)
+		goto fail_no_put;
+	param_set = ipu3_css_pool_last(&css->pool.parameter_set_info, 0)->vaddr;
+
+	map = ipu3_css_pool_last(&css->pool.acc, 0);
+	/* Get a new acc only if new parameters given, or none yet */
+	if (set_params || !map->vaddr) {
+		if (ipu3_css_pool_get(&css->pool.acc, css->frame) < 0)
+			goto fail;
+		map = ipu3_css_pool_last(&css->pool.acc, 0);
+		acc = map->vaddr;
+	}
+
+	/* Get new VMEM0 only if needed, or none yet */
+	m = IMGU_ABI_MEM_ISP_VMEM0;
+	map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 0);
+	if (!map->vaddr || (set_params && (set_params->use.lin_vmem_params ||
+					   set_params->use.tnr3_vmem_params ||
+					   set_params->use.xnr3_vmem_params))) {
+		if (ipu3_css_pool_get(&css->pool.binary_params_p[m],
+				      css->frame) < 0)
+			goto fail;
+		map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 0);
+		vmem0 = map->vaddr;
+	}
+
+	/* Get new DMEM0 only if needed, or none yet */
+	m = IMGU_ABI_MEM_ISP_DMEM0;
+	map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 0);
+	if (!map->vaddr || (set_params && (set_params->use.tnr3_dmem_params ||
+					   set_params->use.xnr3_dmem_params))) {
+		if (ipu3_css_pool_get(&css->pool.binary_params_p[m],
+				      css->frame) < 0)
+			goto fail;
+		map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 0);
+		dmem0 = map->vaddr;
+	}
+
+	/* Configure acc parameter cluster */
+	if (acc) {
+		map = ipu3_css_pool_last(&css->pool.acc, 1);
+		r = ipu3_css_cfg_acc(css, use, acc, map->vaddr, set_params ?
+				     &set_params->acc_param : NULL);
+		if (r < 0)
+			goto fail;
+	}
+
+	/* Configure late binding parameters */
+	if (vmem0) {
+		m = IMGU_ABI_MEM_ISP_VMEM0;
+		map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 1);
+		r = ipu3_css_cfg_vmem0(css, use, vmem0, map->vaddr, set_params);
+		if (r < 0)
+			goto fail;
+	}
+
+	if (dmem0) {
+		m = IMGU_ABI_MEM_ISP_DMEM0;
+		map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 1);
+		r = ipu3_css_cfg_dmem0(css, use, dmem0, map->vaddr, set_params);
+		if (r < 0)
+			goto fail;
+	}
+
+	/* Get a new gdc only if a new gdc is given, or none yet */
+	if (bi->info.isp.sp.enable.dvs_6axis) {
+		unsigned int a = IPU3_CSS_AUX_FRAME_REF;
+		unsigned int g = IPU3_CSS_RECT_GDC;
+		unsigned int e = IPU3_CSS_RECT_ENVELOPE;
+
+		map = ipu3_css_pool_last(&css->pool.gdc, 0);
+
+		if (set_params && !set_params->use.gdc)
+			set_gdc = NULL;
+		if (set_gdc || !map->vaddr) {
+			if (ipu3_css_pool_get(&css->pool.gdc, css->frame) < 0)
+				goto fail;
+			map = ipu3_css_pool_last(&css->pool.gdc, 0);
+			gdc = map->vaddr;
+			ipu3_css_cfg_gdc_table(gdc,
+					       css->aux_frames[a].bytesperline /
+					       css->aux_frames[a].bytesperpixel,
+					       css->aux_frames[a].height,
+					       css->rect[g].width,
+					       css->rect[g].height,
+					       css->rect[e].width + FILTER_SIZE,
+					       css->rect[e].height +
+					       FILTER_SIZE);
+		}
+	}
+
+	/* Get a new obgrid only if a new obgrid is given, or none yet */
+	if (set_params && !set_params->use.obgrid)
+		set_obgrid = NULL;
+	if (set_obgrid && obgrid_bytes < obgrid_size / stripes)
+		goto fail;
+	map = ipu3_css_pool_last(&css->pool.obgrid, 0);
+	if (!map->vaddr || set_obgrid || (set_params &&
+					  set_params->use.obgrid_param)) {
+		if (ipu3_css_pool_get(&css->pool.obgrid, css->frame) < 0)
+			goto fail;
+		map = ipu3_css_pool_last(&css->pool.obgrid, 0);
+		obgrid = map->vaddr;
+
+		/* Configure optical black level grid (obgrid) */
+		if (set_obgrid) {
+			for (i = 0; i < stripes; i++)
+				memcpy((void *)obgrid +
+					(obgrid_size / stripes) * i, set_obgrid,
+					obgrid_size / stripes);
+
+		} else if (set_params && set_params->use.obgrid_param) {
+			for (i = 0; i < obgrid_size / sizeof(*obgrid); i++)
+				obgrid[i] = set_params->obgrid_param;
+		} else {
+			memset(obgrid, 0, obgrid_size);
+		}
+	}
+
+	/* Configure parameter set info, queued to `queue_id' */
+
+	memset(param_set, 0, sizeof(*param_set));
+	map = ipu3_css_pool_last(&css->pool.acc, 0);
+	param_set->mem_map.acc_cluster_params_for_sp = map->daddr;
+
+	map = ipu3_css_pool_last(&css->pool.gdc, 0);
+	param_set->mem_map.dvs_6axis_params_y = map->daddr;
+
+	map = ipu3_css_pool_last(&css->pool.obgrid, 0);
+	for (i = 0; i < stripes; i++)
+		param_set->mem_map.obgrid_tbl[i] =
+				map->daddr + (obgrid_size / stripes) * i;
+
+	for (m = 0; m < IMGU_ABI_NUM_MEMORIES; m++) {
+		map = ipu3_css_pool_last(&css->pool.binary_params_p[m], 0);
+		param_set->mem_map.isp_mem_param[stage][m] = map->daddr;
+	}
+	/* Then queue the new parameter buffer */
+	map = ipu3_css_pool_last(&css->pool.parameter_set_info, 0);
+	r = ipu3_css_queue_data(css, queue_id, thread, map->daddr);
+	if (r < 0)
+		goto fail;
+
+	r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, 0,
+				IMGU_ABI_EVENT_BUFFER_ENQUEUED(thread,
+							       queue_id));
+	if (r < 0)
+		goto fail_no_put;
+
+	/* Finally dequeue all old parameter buffers */
+
+	do {
+		u32 daddr;
+
+		r = ipu3_css_dequeue_data(css, queue_id, &daddr);
+		if (r == -EBUSY)
+			break;
+		if (r)
+			goto fail_no_put;
+		r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, thread,
+					IMGU_ABI_EVENT_BUFFER_DEQUEUED
+					(queue_id));
+		if (r < 0) {
+			dev_err(css->dev, "failed to queue parameter event\n");
+			goto fail_no_put;
+		}
+	} while (1);
+
+	return 0;
+
+fail:
+	/*
+	 * A failure, most likely the parameter queue was full.
+	 * Return error but continue streaming. User can try submitting new
+	 * parameters again later.
+	 */
+
+	ipu3_css_pool_put(&css->pool.parameter_set_info);
+	if (acc)
+		ipu3_css_pool_put(&css->pool.acc);
+	if (gdc)
+		ipu3_css_pool_put(&css->pool.gdc);
+	if (obgrid)
+		ipu3_css_pool_put(&css->pool.obgrid);
+	if (vmem0)
+		ipu3_css_pool_put(
+			&css->pool.binary_params_p[IMGU_ABI_MEM_ISP_VMEM0]);
+	if (dmem0)
+		ipu3_css_pool_put(
+			&css->pool.binary_params_p[IMGU_ABI_MEM_ISP_DMEM0]);
+
+fail_no_put:
+	return r;
+}
+
 int ipu3_css_irq_ack(struct ipu3_css *css)
 {
 	static const int NUM_SWIRQS = 3;
-- 
2.7.4

  parent reply	other threads:[~2017-12-02  4:34 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-02  4:32 [PATCH v5 00/12] Intel IPU3 ImgU patchset Yong Zhi
2017-12-02  4:32 ` [PATCH v5 01/12] v4l: Add Intel IPU3 meta buffer formats Yong Zhi
2017-12-02  4:32 ` [PATCH v5 02/12] intel-ipu3: Add user space ABI definitions Yong Zhi
2017-12-05 11:09   ` Sakari Ailus
2017-12-02  4:32 ` [PATCH v5 03/12] intel-ipu3: mmu: Implement driver Yong Zhi
2017-12-02  4:32 ` [PATCH v5 04/12] intel-ipu3: Implement DMA mapping functions Yong Zhi
2017-12-02  4:32 ` [PATCH v5 05/12] intel-ipu3: css: Add dma buff pool utility functions Yong Zhi
2017-12-02  4:32 ` [PATCH v5 06/12] intel-ipu3: css: Add support for firmware management Yong Zhi
2017-12-02  4:32 ` [PATCH v5 08/12] intel-ipu3: css: Compute and program ccs Yong Zhi
2017-12-02  4:32 ` [PATCH v5 09/12] intel-ipu3: css: Initialize css hardware Yong Zhi
2017-12-02  4:32 ` Yong Zhi [this message]
2017-12-02  4:32 ` [PATCH v5 11/12] intel-ipu3: Add v4l2 driver based on media framework Yong Zhi
2017-12-02  4:32 ` [PATCH v5 12/12] intel-ipu3: Add imgu top level pci device driver Yong Zhi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1512189142-19863-11-git-send-email-yong.zhi@intel.com \
    --to=yong.zhi@intel.com \
    --cc=chiranjeevi.rapolu@intel.com \
    --cc=hyungwoo.yang@intel.com \
    --cc=jerry.w.hu@intel.com \
    --cc=jian.xu.zheng@intel.com \
    --cc=linux-media@vger.kernel.org \
    --cc=rajmohan.mani@intel.com \
    --cc=sakari.ailus@linux.intel.com \
    --cc=tfiga@chromium.org \
    --cc=tuukka.toivonen@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.