/****************************************************************************
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2020 VeriSilicon Holdings Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 *****************************************************************************
 *
 * The GPL License (GPL)
 *
 * Copyright (c) 2020 VeriSilicon Holdings Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program;
 *
 *****************************************************************************
 *
 * Note: This software is released under dual MIT and GPL licenses. A
 * recipient may use this file under the terms of either the MIT license or
 * GPL License. If you wish to use only one license not the other, you can
 * indicate your decision by deleting one of the above license notices in your
 * version of this file.
 *
 *****************************************************************************/
#include "mrv_all_bits.h"
#include "isp_ioctl.h"
#include "isp_types.h"

#ifdef ISP_MIV1

extern MrvAllRegister_t *all_regs;

static int getRawBit(u32 type, u32 *bit, u32 *len)
{
	*len = 16;
	switch (type) {
	case ISP_PICBUF_TYPE_RAW8:
		*bit = 0;
		*len = 8;
		break;
#if 0				/* normal process,  need pass type from engine. */
	case ISP_PICBUF_TYPE_RAW10:
		*bit = 1;
		break;
	case ISP_PICBUF_TYPE_RAW12:
		*bit = 2;
		break;
	case ISP_PICBUF_TYPE_RAW14:
		*bit = 3;
		break;
	case ISP_PICBUF_TYPE_RAW16:
		*bit = 4;
		break;
#else /* WA */
	case ISP_PICBUF_TYPE_RAW10:
	case ISP_PICBUF_TYPE_RAW12:
	case ISP_PICBUF_TYPE_RAW14:
	case ISP_PICBUF_TYPE_RAW16:
		*bit = 4;
		break;
#endif
	default:
		isp_err("unsupport raw formt: %d\n", type);
		return -1;
	}
	return 0;
}
int isp_ioc_cfg_dma(struct isp_ic_dev *dev, void __user *args)
{
	return 0;
}
int isp_ioc_start_dma_read(struct isp_ic_dev *dev, void __user *args)
{
	struct isp_dma_context dma;
	u32 mi_dma_ctrl = isp_read_reg(dev, REG_ADDR(mi_dma_ctrl));
	u32 llength = 0, mcm_rd_fmt_bit = 0;
	u32 mi_imsc = 0, mcm_fmt = 0;

	isp_info("enter %s\n", __func__);
	viv_check_retval(copy_from_user(&dma, args, sizeof(dma)));

	REG_SET_SLICE(mi_dma_ctrl, MRV_MI_DMA_BURST_LEN_LUM, dma.burst_y);
	REG_SET_SLICE(mi_dma_ctrl, MRV_MI_DMA_BURST_LEN_CHROM, dma.burst_c);

	isp_write_reg(dev, REG_ADDR(mi_dma_y_pic_start_ad),
		      (MRV_MI_DMA_Y_PIC_START_AD_MASK & dma.base));
	getRawBit(dma.type, &mcm_rd_fmt_bit, &llength);

	llength = dma.width * llength / 8;
	REG_SET_SLICE(mcm_fmt, MCM_RD_RAW_BIT, mcm_rd_fmt_bit);
	isp_write_reg(dev, REG_ADDR(mi_dma_y_pic_width),
		      (MRV_MI_DMA_Y_PIC_WIDTH_MASK & dma.width));
	isp_write_reg(dev, REG_ADDR(mi_dma_y_llength),
		      (MRV_MI_DMA_Y_LLENGTH_MASK & llength));
	isp_write_reg(dev, REG_ADDR(mi_dma_y_pic_size),
		      (MRV_MI_DMA_Y_PIC_SIZE_MASK & (llength * dma.height)));
	isp_write_reg(dev, REG_ADDR(mi_dma_cb_pic_start_ad), 0);
	isp_write_reg(dev, REG_ADDR(mi_dma_cr_pic_start_ad), 0);
	isp_write_reg(dev, REG_ADDR(mi_dma_ctrl), mi_dma_ctrl);

	isp_write_reg(dev, REG_ADDR(mi_dma_status), 0);
	isp_write_reg(dev, REG_ADDR(mi_dma_y_raw_fmt), mcm_fmt);
	isp_write_reg(dev, REG_ADDR(mi_dma_y_raw_lval),
		      (MRV_MI_DMA_Y_LLENGTH_MASK & llength));

	mi_imsc = isp_read_reg(dev, REG_ADDR(mi_imsc));
	mi_imsc |= MRV_MI_DMA_READY_MASK;

	isp_write_reg(dev, REG_ADDR(mi_imsc), mi_imsc);
	isp_write_reg(dev, REG_ADDR(mi_dma_start), MRV_MI_DMA_START_MASK);
	return 0;
}

u32 getScaleFactor(u32 src, u32 dst)
{
	if (dst > src) {
		return ((65536 * (src - 1)) / (dst - 1));
	} else if (dst < src) {
		return ((65536 * (dst - 1)) / (src - 1)) + 1;
	}
	return 65536;
}

int set_scaling(int id, struct isp_ic_dev *dev, bool stabilization)
{
	u32 addr, ctrl;
	u32 iw, ih, ow, oh;
	u32 inputWidth, inputHeight, outputWidth, outputHeight;
	u32 scale_hy, scale_hcb, scale_hcr, scale_vy, scale_vc;
	struct isp_mi_data_path_context *path = &dev->mi.path[id];

	if (id == IC_MI_PATH_MAIN) {	/* mp */
		addr = REG_ADDR(mrsz_ctrl);
	} else if (id == IC_MI_PATH_SELF) {	/* sp */
		addr = REG_ADDR(srsz_ctrl);
	} else {
		return -EINVAL;
	}

	inputWidth = path->in_width;
	inputHeight = path->in_height;
	outputWidth = path->out_width;
	outputHeight = path->out_height;

	if (stabilization) {	/* enabled image stabilization. */
		inputWidth = isp_read_reg(dev, REG_ADDR(isp_is_h_size));
		inputHeight = isp_read_reg(dev, REG_ADDR(isp_is_v_size));
	}

	ctrl = isp_read_reg(dev, addr);
	iw = inputWidth / 2;
	ih = inputHeight;
	ow = outputWidth / 2;
	oh = outputHeight;

	switch (path->in_mode) {
	case IC_MI_DATAMODE_YUV422:
		oh = outputHeight;
		break;
	case IC_MI_DATAMODE_YUV420:
		oh = outputHeight / 2;	/*  scale cbcr */
		break;
	default:
		return -EFAULT;
	}

	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_HY_ENABLE,
		      inputWidth != outputWidth);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_VY_ENABLE,
		      inputHeight != outputHeight);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_HY_UP, inputWidth < outputWidth);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_VY_UP, inputHeight < outputHeight);
	scale_hy = getScaleFactor(inputWidth, outputWidth);
	scale_vy = getScaleFactor(inputHeight, outputHeight);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_HC_ENABLE, iw != ow);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_VC_ENABLE, ih != oh);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_HC_UP, iw < ow);
	REG_SET_SLICE(ctrl, MRV_MRSZ_SCALE_VC_UP, ih < oh);
	scale_hcr = getScaleFactor(iw, ow);
	scale_hcb = getScaleFactor(iw, ow);
	scale_vc = getScaleFactor(ih, oh);

	REG_SET_SLICE(ctrl, MRV_MRSZ_AUTO_UPD, 1);

	if (id == IC_MI_PATH_MAIN) {
		isp_write_reg(dev, REG_ADDR(mrsz_scale_vc), scale_vc);
		isp_write_reg(dev, REG_ADDR(mrsz_scale_vy), scale_vy);
		isp_write_reg(dev, REG_ADDR(mrsz_scale_hcr), scale_hcr);
		isp_write_reg(dev, REG_ADDR(mrsz_scale_hcb), scale_hcb);
		isp_write_reg(dev, REG_ADDR(mrsz_scale_hy), scale_hy);
		isp_write_reg(dev, REG_ADDR(mrsz_ctrl),
			      ctrl | MRV_MRSZ_CFG_UPD_MASK);
	} else if (id == IC_MI_PATH_SELF) {
		isp_write_reg(dev, REG_ADDR(srsz_scale_vc), scale_vc);
		isp_write_reg(dev, REG_ADDR(srsz_scale_vy), scale_vy);
		isp_write_reg(dev, REG_ADDR(srsz_scale_hcr), scale_hcr);
		isp_write_reg(dev, REG_ADDR(srsz_scale_hcb), scale_hcb);
		isp_write_reg(dev, REG_ADDR(srsz_scale_hy), scale_hy);
		isp_write_reg(dev, REG_ADDR(srsz_ctrl),
			      ctrl | MRV_MRSZ_CFG_UPD_MASK);
	}

	return 0;
}

#ifdef ISP_MI_BP
int isp_bppath_start(struct isp_ic_dev *dev)
{
	struct isp_mi_context mi = *(&dev->mi);
	u32 bp_ctrl = 0, lval = 0;
	struct isp_mi_data_path_context *path = &mi.path[2];
	u32 mi_imsc = isp_read_reg(dev, REG_ADDR(mi_imsc));
	int i;

	isp_info("enter %s\n", __func__);
	bp_ctrl = 0;
	lval = path->out_width;

	if (mi.path[2].enable) {
		bp_ctrl &= ~MRV_MI_BP_WRITE_RAWBIT_MASK;

		if (path->data_alignMode == ISP_MI_DATA_ALIGN_16BIT_MODE) {
			if ((path->out_mode == IC_MI_DATAMODE_RAW10) ||
			    (path->out_mode == IC_MI_DATAMODE_RAW12) ||
			    (path->out_mode == IC_MI_DATAMODE_RAW14)) {
				lval = (path->out_width + 3) / 4;
			}
		} else if (path->data_alignMode ==
			   ISP_MI_DATA_ALIGN_128BIT_MODE) {
			if ((path->out_mode == IC_MI_DATAMODE_RAW10)
			    || (path->out_mode == IC_MI_DATAMODE_RAW12)
			    || (path->out_mode == IC_MI_DATAMODE_RAW14)) {
				lval = (path->out_width * 2 + 126) / 128;
			}
		} else {
			if (path->out_mode == IC_MI_DATAMODE_RAW10) {
				lval = (path->out_width * 10 + 63) / 64;
			} else if (path->out_mode == IC_MI_DATAMODE_RAW12) {
				lval = (path->out_width * 12 + 63) / 64;
			} else if (path->out_mode == IC_MI_DATAMODE_RAW14) {
				lval = (path->out_width * 14 + 63) / 64;
			} else if (path->out_mode == IC_MI_DATAMODE_RAW16) {
				lval = (path->out_width * 16 + 63) / 64;
			} else {
				lval = (path->out_width * 8 + 63) / 64;
			}
		}
		lval <<= 3;
		REG_SET_SLICE(bp_ctrl, BP_WR_RAW_ALIGNED, path->data_alignMode);
		switch (mi.path[2].out_mode) {
		case (IC_MI_DATAMODE_RAW8):
			REG_SET_SLICE(bp_ctrl, MRV_MI_BP_WRITE_RAWBIT,
				      MRV_MI_BP_WRITE_RAWBIT_RAW_8);
			REG_SET_SLICE(bp_ctrl, MRV_MI_BP_WRITE_FORMAT,
				      MRV_MI_BP_WRITE_INTERLEAVE_FORMAT);
			break;
		case (IC_MI_DATAMODE_RAW12):
			REG_SET_SLICE(bp_ctrl, BP_WR_BYTE_SWAP, 1);
			REG_SET_SLICE(bp_ctrl, MRV_MI_BP_WRITE_RAWBIT,
				      MRV_MI_BP_WRITE_RAWBIT_RAW_12);
			REG_SET_SLICE(bp_ctrl, MRV_MI_BP_WRITE_FORMAT,
				      MRV_MI_BP_WRITE_INTERLEAVE_FORMAT);
			break;
		case (IC_MI_DATAMODE_RAW10):
			REG_SET_SLICE(bp_ctrl, BP_WR_BYTE_SWAP, 1);
			REG_SET_SLICE(bp_ctrl, MRV_MI_BP_WRITE_RAWBIT,
				      MRV_MI_BP_WRITE_RAWBIT_RAW_10);
			REG_SET_SLICE(bp_ctrl, MRV_MI_BP_WRITE_FORMAT,
				      MRV_MI_BP_WRITE_INTERLEAVE_FORMAT);
			break;
		default:
			break;
		}
		isp_write_reg(dev, REG_ADDR(mi_bp_wr_size_init),
			      lval * mi.path[2].out_height);
		isp_write_reg(dev, REG_ADDR(mi_bp_pic_width),
			      mi.path[2].out_width);
		isp_write_reg(dev, REG_ADDR(mi_bp_wr_llength), lval);
		isp_write_reg(dev, REG_ADDR(mi_bp_pic_height),
			      mi.path[2].out_height);
		isp_write_reg(dev, REG_ADDR(mi_bp_pic_size),
			      lval * mi.path[2].out_height);
		/* enable frame end irq for  bp path */
		mi_imsc |=
		    MRV_MI_BP_FRAME_END_MASK | MRV_MI_BP_WRAP_R_MASK |
		    MRV_MI_BP_WRAP_GR_MASK | MRV_MI_BP_WRAP_GB_MASK |
		    MRV_MI_BP_WRAP_B_MASK;
	}
	if (!dev->rawis.enable) {
		isp_write_reg(dev, REG_ADDR(isp_raw_is_h_size),
			      mi.path[2].out_width);
		isp_write_reg(dev, REG_ADDR(isp_raw_is_v_size),
			      mi.path[2].out_height);
		isp_write_reg(dev, REG_ADDR(isp_raw_is_ctrl), 0);
	}
	bp_ctrl |= MRV_MI_BP_PATH_ENABLE_MASK;
	isp_write_reg(dev, REG_ADDR(mi_bp_ctrl), bp_ctrl);

	isp_write_reg(dev, REG_ADDR(mi_imsc), mi_imsc);
	return 0;
}
#endif
int isp_mi_start(struct isp_ic_dev *dev)
{
	struct isp_mi_context mi = *(&dev->mi);
	u32 mi_init, mi_ctrl, mi_imsc;
	u32 out_stride;
	int i;
	u8 retry = 3;

	isp_info("enter %s\n", __func__);

	isp_write_reg(dev, REG_ADDR(mrsz_ctrl), 0);
	isp_write_reg(dev, REG_ADDR(mrsz_ctrl_shd), 0);

	for (i = 0; i < 2; i++) {
		if (mi.path[i].hscale || mi.path[i].vscale) {
			set_scaling(i, dev, dev->is.enable);
		}
	}

	mi_init = 0;
	mi_ctrl = 0;
	mi_imsc = 0;
	if (mi.path[0].enable) {
		/* remove update enable bits for offset and base registers */
		mi_init &= ~MRV_MI_MP_OUTPUT_FORMAT_MASK;
		mi_ctrl &= ~MRV_MI_MP_WRITE_FORMAT_MASK;

		/* config mi_init output format for yuv format */
		if (mi.path[0].out_mode <= IC_MI_DATAMODE_YUV400)
			REG_SET_SLICE(mi_init, MRV_MI_MP_OUTPUT_FORMAT,
				      IC_MI_DATAMODE_YUV400 -
				      mi.path[0].out_mode);
		switch (mi.path[0].out_mode) {
		case (IC_MI_DATAMODE_RAW8):
			REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
				      MRV_MI_MP_WRITE_FORMAT_RAW_8);
			REG_SET_SLICE(mi_ctrl, MRV_MI_RAW_ENABLE, 1);
			REG_SET_SLICE(mi_init, MRV_MI_MP_OUTPUT_FORMAT,
				      MRV_MI_MP_OUTPUT_FORMAT_RAW8);
			break;
		case (IC_MI_DATAMODE_RAW12):
			REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
				      MRV_MI_MP_WRITE_FORMAT_RAW_12);
			REG_SET_SLICE(mi_ctrl, MRV_MI_RAW_ENABLE, 1);
			REG_SET_SLICE(mi_init, MRV_MI_MP_OUTPUT_FORMAT,
				      MRV_MI_MP_OUTPUT_FORMAT_RAW12);
			isp_write_reg(dev, REG_ADDR(mi_output_align_format), 1);
			break;
		case (IC_MI_DATAMODE_RAW10):
			REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
				      MRV_MI_MP_WRITE_FORMAT_RAW_12);
			REG_SET_SLICE(mi_ctrl, MRV_MI_RAW_ENABLE, 1);
			REG_SET_SLICE(mi_init, MRV_MI_MP_OUTPUT_FORMAT,
				      MRV_MI_MP_OUTPUT_FORMAT_RAW10);
			isp_write_reg(dev, REG_ADDR(mi_output_align_format), 1);
			break;
		case (IC_MI_DATAMODE_JPEG):
			REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
				      MRV_MI_MP_WRITE_FORMAT_PLANAR);
			REG_SET_SLICE(mi_ctrl, MRV_MI_JPEG_ENABLE, 1);
			break;
		case (IC_MI_DATAMODE_YUV444):
		case (IC_MI_DATAMODE_YUV422):
		case (IC_MI_DATAMODE_YUV420):
		case (IC_MI_DATAMODE_YUV400):
			if (mi.path[0].data_layout == IC_MI_DATASTORAGE_PLANAR) {
				REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
					      MRV_MI_MP_WRITE_FORMAT_PLANAR);
			} else if (mi.path[0].data_layout ==
				   IC_MI_DATASTORAGE_SEMIPLANAR) {
				REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
					      MRV_MI_MP_WRITE_FORMAT_SEMIPLANAR);
			} else if (mi.path[0].data_layout ==
				   IC_MI_DATASTORAGE_INTERLEAVED) {
				REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT,
					      MRV_MI_MP_WRITE_FORMAT_INTERLEAVED);
			} else {
				break;
			}
			REG_SET_SLICE(mi_ctrl, MRV_MI_MP_ENABLE, 1);
			break;
		default:
			break;
		}

		out_stride = mi.path[0].data_layout ==
		    IC_MI_DATASTORAGE_INTERLEAVED ?
		    (mi.path[0].out_width * 2) : (mi.path[0].out_width);
		isp_write_reg(dev, REG_ADDR(mi_mp_y_pic_width), out_stride);
		isp_write_reg(dev, REG_ADDR(mi_mp_y_llength), out_stride);
		isp_write_reg(dev, REG_ADDR(mi_mp_y_pic_height),
			      mi.path[0].out_height);
		isp_write_reg(dev, REG_ADDR(mi_mp_y_pic_size),
			      out_stride * mi.path[0].out_height);

		/* workaround to resolve the problem that the mi_mp_y_pic_width can't be written */
		for(i = 0; i < retry; i++) {
			if(isp_read_reg(dev, REG_ADDR(mi_mp_y_pic_width)) != out_stride) {
				isp_write_reg(dev, REG_ADDR(mi_mp_y_pic_width), out_stride);
			} else {
				break;
			}
		}
		if(retry == i) {
			isp_info("%s: update mi_mp_y_pic_width error!\n", __func__);
		}

		/* enable frame end irq for  main path */
		mi_imsc |=
		    (MRV_MI_MP_FRAME_END_MASK | MRV_MI_WRAP_MP_Y_MASK |
		     MRV_MI_WRAP_MP_CB_MASK | MRV_MI_WRAP_MP_CR_MASK);
	}

	if (mi.path[1].enable) {
		/* setup mi for self-path */
		mi_ctrl &= ~(MRV_MI_SP_WRITE_FORMAT_MASK);
		REG_SET_SLICE(mi_ctrl, MRV_MI_SP_INPUT_FORMAT,
			      mi.path[1].in_mode - 1);
		REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT,
			      mi.path[1].out_mode - 1);

		switch (mi.path[1].out_mode) {
		case (IC_MI_DATAMODE_RGB888):
		case (IC_MI_DATAMODE_RGB666):
		case (IC_MI_DATAMODE_RGB565):
			REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
				      MRV_MI_SP_WRITE_FORMAT_RGB_INTERLEAVED);
			break;
		case (IC_MI_DATAMODE_YUV444):
		case (IC_MI_DATAMODE_YUV400):
			if (mi.path[1].data_layout == IC_MI_DATASTORAGE_PLANAR) {
				REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
					      MRV_MI_SP_WRITE_FORMAT_PLANAR);
			}
			break;
		case (IC_MI_DATAMODE_YUV422):
			switch (mi.path[1].data_layout) {
			case (IC_MI_DATASTORAGE_PLANAR):
				REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
					      MRV_MI_SP_WRITE_FORMAT_PLANAR);
				break;
			case (IC_MI_DATASTORAGE_SEMIPLANAR):
				REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
					      MRV_MI_SP_WRITE_FORMAT_SEMIPLANAR);
				break;
			case (IC_MI_DATASTORAGE_INTERLEAVED):
				REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
					      MRV_MI_SP_WRITE_FORMAT_INTERLEAVED);
				break;
			default:
				break;
			}
			break;
		case (IC_MI_DATAMODE_YUV420):
			switch (mi.path[1].data_layout) {
			case (IC_MI_DATASTORAGE_PLANAR):
				REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
					      MRV_MI_SP_WRITE_FORMAT_PLANAR);
				break;
			case (IC_MI_DATASTORAGE_SEMIPLANAR):
				REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT,
					      MRV_MI_SP_WRITE_FORMAT_SEMIPLANAR);
				break;
			default:
				break;
			}
			break;
		default:
			break;
		}

		out_stride = mi.path[1].data_layout ==
		    IC_MI_DATASTORAGE_INTERLEAVED ?
		    mi.path[1].out_width * 2 : mi.path[1].out_width;
		REG_SET_SLICE(mi_ctrl, MRV_MI_SP_ENABLE, 1);
		isp_write_reg(dev, REG_ADDR(mi_sp_y_pic_width), out_stride);
		isp_write_reg(dev, REG_ADDR(mi_sp_y_llength), out_stride);
		isp_write_reg(dev, REG_ADDR(mi_sp_y_pic_height),
			      mi.path[1].out_height);
		isp_write_reg(dev, REG_ADDR(mi_sp_y_pic_size),
			      out_stride * mi.path[1].out_height);
		/* enable frame end interrupt on self path */
		mi_imsc |=
		    (MRV_MI_SP_FRAME_END_MASK | MRV_MI_WRAP_SP_Y_MASK |
		     MRV_MI_WRAP_SP_CB_MASK | MRV_MI_WRAP_SP_CR_MASK);
	}

#if defined(__KERNEL__) && defined(ENABLE_IRQ)
	for (i = 0; i < MI_PATH_NUM; ++i) {
		dev->mi_buf[i] = NULL;
		dev->mi_buf_shd[i] = NULL;
	}
	if (dev->state)
		*dev->state |= STATE_DRIVER_STARTED;
#endif

	mi_ctrl |= (MRV_MI_INIT_BASE_EN_MASK | MRV_MI_INIT_OFFSET_EN_MASK);
	REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_CHROM, mi.burst_len);
	REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_LUM, mi.burst_len);
	isp_write_reg(dev, REG_ADDR(mi_ctrl), mi_ctrl | 0x2000);
	REG_SET_SLICE(mi_init, MRV_MI_MI_CFG_UPD, 1);

	isp_write_reg(dev, REG_ADDR(mi_imsc), mi_imsc);
#ifdef ISP_MI_BP
	isp_bppath_start(dev);
#endif
	isp_write_reg(dev, REG_ADDR(mi_init), mi_init);
	return 0;
}

int isp_mi_stop(struct isp_ic_dev *dev)
{
	u32 mi_ctrl = 0, mi_init = 0;
	isp_info("enter %s\n", __func__);

	isp_write_reg(dev, REG_ADDR(mi_imsc), 0);

	/* disable mi path */
	mi_ctrl = isp_read_reg(dev, REG_ADDR(mi_ctrl));
	REG_SET_SLICE(mi_ctrl, MRV_MI_MP_ENABLE, 0);
	REG_SET_SLICE(mi_ctrl, MRV_MI_SP_ENABLE, 0);
	REG_SET_SLICE(mi_ctrl, MRV_MI_JPEG_ENABLE, 0);
	REG_SET_SLICE(mi_ctrl, MRV_MI_RAW_ENABLE, 0);
	isp_write_reg(dev, REG_ADDR(mi_ctrl), mi_ctrl);

	mi_init = isp_read_reg(dev, REG_ADDR(mi_init));
	REG_SET_SLICE(mi_init, MRV_MI_MI_CFG_UPD, 1);
	isp_write_reg(dev, REG_ADDR(mi_init), mi_init);

#if defined(__KERNEL__) && defined(ENABLE_IRQ)
	if (dev->state)
		*dev->state &= ~STATE_DRIVER_STARTED;
	clean_dma_buffer(dev);
#endif
	return 0;
}

int isp_set_buffer(struct isp_ic_dev *dev, struct isp_buffer_context *buf)
{
	u32 addr;

	if (!dev || !buf) {
		isp_err("NULL pointer %s\n", __func__);
		return -EINVAL;
	}

	addr = buf->path == 0 ?
	    REG_ADDR(mi_mp_y_base_ad_init) : REG_ADDR(mi_sp_y_base_ad_init);
	isp_write_reg(dev, addr, (buf->addr_y & MRV_MI_MP_Y_BASE_AD_INIT_MASK));
	isp_write_reg(dev, addr + 1 * 4,
		      (buf->size_y & MRV_MI_MP_Y_SIZE_INIT_MASK));
	isp_write_reg(dev, addr + 2 * 4, 0);
	isp_write_reg(dev, addr + 5 * 4,
		      (buf->addr_cb & MRV_MI_MP_CB_BASE_AD_INIT_MASK));
	isp_write_reg(dev, addr + 6 * 4,
		      (buf->size_cb & MRV_MI_MP_CB_SIZE_INIT_MASK));
	isp_write_reg(dev, addr + 7 * 4, 0);
	isp_write_reg(dev, addr + 9 * 4,
		      (buf->addr_cr & MRV_MI_MP_CR_BASE_AD_INIT_MASK));
	isp_write_reg(dev, addr + 10 * 4,
		      (buf->size_cr & MRV_MI_MP_CR_SIZE_INIT_MASK));
	isp_write_reg(dev, addr + 11 * 4, 0);

	return 0;
}

int isp_set_bp_buffer(struct isp_ic_dev *dev, struct isp_bp_buffer_context *buf)
{
#ifndef ISP_MI_BP
	isp_err("unsupported function: %s", __func__);
	return -EINVAL;
#else
	isp_write_reg(dev, REG_ADDR(mi_bp_r_base_ad_init),
		      (buf->addr_r & BP_R_BASE_AD_INIT_MASK));
	isp_write_reg(dev, REG_ADDR(mi_bp_gr_base_ad_init),
		      (buf->addr_gr & BP_GR_BASE_AD_INIT_MASK));

	isp_write_reg(dev, REG_ADDR(mi_bp_gb_base_ad_init),
		      (buf->addr_gb & BP_GB_BASE_AD_INIT_MASK));
	isp_write_reg(dev, REG_ADDR(mi_bp_b_base_ad_init),
		      (buf->addr_b & BP_B_BASE_AD_INIT_MASK));
	return 0;
#endif
}

u32 isp_read_mi_irq(struct isp_ic_dev * dev)
{
	return isp_read_reg(dev, REG_ADDR(mi_mis));
}

void isp_reset_mi_irq(struct isp_ic_dev *dev, u32 icr)
{
	isp_write_reg(dev, REG_ADDR(mi_icr), icr);
}

#endif
