/* Copyright (c) 2009, 2012, Code Aurora Forum. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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.
 *
 */

#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <mach/irqs.h>
#include "msm_vfe8x_proc.h"
#include <linux/pm_qos.h>

#define ON  1
#define OFF 0

static const char *vfe_general_cmd[] = {
	"START",  /* 0 */
	"RESET",
	"AXI_INPUT_CONFIG",
	"CAMIF_CONFIG",
	"AXI_OUTPUT_CONFIG",
	"BLACK_LEVEL_CONFIG",  /* 5 */
	"ROLL_OFF_CONFIG",
	"DEMUX_CHANNEL_GAIN_CONFIG",
	"DEMOSAIC_CONFIG",
	"FOV_CROP_CONFIG",
	"MAIN_SCALER_CONFIG",  /* 10 */
	"WHITE_BALANCE_CONFIG",
	"COLOR_CORRECTION_CONFIG",
	"LA_CONFIG",
	"RGB_GAMMA_CONFIG",
	"CHROMA_ENHAN_CONFIG",  /* 15 */
	"CHROMA_SUPPRESSION_CONFIG",
	"ASF_CONFIG",
	"SCALER2Y_CONFIG",
	"SCALER2CbCr_CONFIG",
	"CHROMA_SUBSAMPLE_CONFIG",  /* 20 */
	"FRAME_SKIP_CONFIG",
	"OUTPUT_CLAMP_CONFIG",
	"TEST_GEN_START",
	"UPDATE",
	"OUTPUT1_ACK",  /* 25 */
	"OUTPUT2_ACK",
	"EPOCH1_ACK",
	"EPOCH2_ACK",
	"STATS_AUTOFOCUS_ACK",
	"STATS_WB_EXP_ACK",  /* 30 */
	"BLACK_LEVEL_UPDATE",
	"DEMUX_CHANNEL_GAIN_UPDATE",
	"DEMOSAIC_BPC_UPDATE",
	"DEMOSAIC_ABF_UPDATE",
	"FOV_CROP_UPDATE",  /* 35 */
	"WHITE_BALANCE_UPDATE",
	"COLOR_CORRECTION_UPDATE",
	"LA_UPDATE",
	"RGB_GAMMA_UPDATE",
	"CHROMA_ENHAN_UPDATE",  /* 40 */
	"CHROMA_SUPPRESSION_UPDATE",
	"MAIN_SCALER_UPDATE",
	"SCALER2CbCr_UPDATE",
	"SCALER2Y_UPDATE",
	"ASF_UPDATE",  /* 45 */
	"FRAME_SKIP_UPDATE",
	"CAMIF_FRAME_UPDATE",
	"STATS_AUTOFOCUS_UPDATE",
	"STATS_WB_EXP_UPDATE",
	"STOP",  /* 50 */
	"GET_HW_VERSION",
	"STATS_SETTING",
	"STATS_AUTOFOCUS_START",
	"STATS_AUTOFOCUS_STOP",
	"STATS_WB_EXP_START",  /* 55 */
	"STATS_WB_EXP_STOP",
	"ASYNC_TIMER_SETTING",
};

static void     *vfe_syncdata;

static int vfe_enable(struct camera_enable_cmd *enable)
{
	return 0;
}

static int vfe_disable(struct camera_enable_cmd *enable,
	struct platform_device *dev)
{
	vfe_stop();
	msm_camio_disable(dev);
	return 0;
}

static void vfe_release(struct platform_device *dev)
{
	msm_camio_disable(dev);
	vfe_cmd_release(dev);
	update_axi_qos(PM_QOS_DEFAULT_VALUE);
	vfe_syncdata = NULL;
}

static void vfe_config_axi(int mode,
			   struct axidata *ad,
			   struct vfe_cmd_axi_output_config *ao)
{
	struct msm_pmem_region *regptr, *regptr1;
	int i, j;
	uint32_t *p1, *p2;

	if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
		regptr = ad->region;
		for (i = 0; i < ad->bufnum1; i++) {

			p1 = &(ao->output1.outputY.outFragments[i][0]);
			p2 = &(ao->output1.outputCbcr.outFragments[i][0]);

			for (j = 0; j < ao->output1.fragmentCount; j++) {

				*p1 = regptr->paddr + regptr->info.planar0_off;
				p1++;

				*p2 = regptr->paddr + regptr->info.planar1_off;
				p2++;
			}
			regptr++;
		}
	} /* if OUTPUT1 or Both */

	if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {

		regptr = &(ad->region[ad->bufnum1]);
		CDBG("bufnum2 = %d\n", ad->bufnum2);

		for (i = 0; i < ad->bufnum2; i++) {

			p1 = &(ao->output2.outputY.outFragments[i][0]);
			p2 = &(ao->output2.outputCbcr.outFragments[i][0]);

			CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\
			     "cbcr_off = %d\n", regptr->paddr,
				regptr->info.planar0_off,
				regptr->info.planar1_off);

			for (j = 0; j < ao->output2.fragmentCount; j++) {

				*p1 = regptr->paddr + regptr->info.planar0_off;
				CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
				p1++;

				*p2 = regptr->paddr + regptr->info.planar1_off;
				CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
				p2++;
			}
			regptr++;
		}
	}
	/* For video configuration */
	if (mode == OUTPUT_1_AND_3) {
		/* this is preview buffer. */
		regptr =  &(ad->region[0]);
		/* this is video buffer. */
		regptr1 = &(ad->region[ad->bufnum1]);
		CDBG("bufnum1 = %d\n", ad->bufnum1);
		CDBG("bufnum2 = %d\n", ad->bufnum2);

	for (i = 0; i < ad->bufnum1; i++) {
		p1 = &(ao->output1.outputY.outFragments[i][0]);
		p2 = &(ao->output1.outputCbcr.outFragments[i][0]);

		CDBG("config_axi: O1, phy = 0x%lx, y_off = %d, "\
			 "cbcr_off = %d\n", regptr->paddr,
			 regptr->info.planar0_off, regptr->info.planar1_off);

			for (j = 0; j < ao->output1.fragmentCount; j++) {

				*p1 = regptr->paddr + regptr->info.planar0_off;
				CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
				p1++;

				*p2 = regptr->paddr + regptr->info.planar1_off;
				CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
				p2++;
			}
			regptr++;
		}
	for (i = 0; i < ad->bufnum2; i++) {
		p1 = &(ao->output2.outputY.outFragments[i][0]);
		p2 = &(ao->output2.outputCbcr.outFragments[i][0]);

		CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\
			 "cbcr_off = %d\n", regptr1->paddr,
			 regptr1->info.planar0_off, regptr1->info.planar1_off);

			for (j = 0; j < ao->output2.fragmentCount; j++) {
				*p1 = regptr1->paddr +
					regptr1->info.planar0_off;
				CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
				p1++;
				*p2 = regptr1->paddr +
					r1->info.planar1_off;
				CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
				p2++;
			}
			regptr1++;
		}
	}

}

#define CHECKED_COPY_FROM_USER(in) {					\
	if (cmd->length != sizeof(*(in))) {				\
		pr_err("msm_camera: %s:%d cmd %d: user data size %d "	\
			"!= kernel data size %d\n",			\
			__func__, __LINE__,				\
			cmd->id, cmd->length, sizeof(*(in)));		\
		rc = -EIO;						\
		break;							\
	}								\
	if (copy_from_user((in), (void __user *)cmd->value,		\
			sizeof(*(in)))) {				\
		rc = -EFAULT;						\
		break;							\
	}								\
}

static int vfe_proc_general(struct msm_vfe_command_8k *cmd)
{
	int rc = 0;

	CDBG("%s: cmdID = %s\n", __func__, vfe_general_cmd[cmd->id]);

	switch (cmd->id) {
	case VFE_CMD_ID_RESET:
		msm_camio_vfe_blk_reset();
		msm_camio_camif_pad_reg_reset_2();
		vfe_reset();
		break;

	case VFE_CMD_ID_START: {
		struct vfe_cmd_start start;
			CHECKED_COPY_FROM_USER(&start);

		/* msm_camio_camif_pad_reg_reset_2(); */
		msm_camio_camif_pad_reg_reset();
		vfe_start(&start);
	}
		break;

	case VFE_CMD_ID_CAMIF_CONFIG: {
		struct vfe_cmd_camif_config camif;
			CHECKED_COPY_FROM_USER(&camif);

		vfe_camif_config(&camif);
	}
		break;

	case VFE_CMD_ID_BLACK_LEVEL_CONFIG: {
		struct vfe_cmd_black_level_config bl;
			CHECKED_COPY_FROM_USER(&bl);

		vfe_black_level_config(&bl);
	}
		break;

	case VFE_CMD_ID_ROLL_OFF_CONFIG:{
			/* rolloff is too big to be on the stack */
			struct vfe_cmd_roll_off_config *rolloff =
			    kmalloc(sizeof(struct vfe_cmd_roll_off_config),
				    GFP_KERNEL);
			if (!rolloff) {
				pr_err("%s: out of memory\n", __func__);
				rc = -ENOMEM;
				break;
			}
			/* Wrap CHECKED_COPY_FROM_USER() in a do-while(0) loop
			 * to make sure we free rolloff when copy_from_user()
			 * fails.
			 */
			do {
				CHECKED_COPY_FROM_USER(rolloff);
				vfe_roll_off_config(rolloff);
			} while (0);
			kfree(rolloff);
	}
		break;

	case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: {
		struct vfe_cmd_demux_channel_gain_config demuxc;
			CHECKED_COPY_FROM_USER(&demuxc);

		/* demux is always enabled.  */
		vfe_demux_channel_gain_config(&demuxc);
	}
		break;

	case VFE_CMD_ID_DEMOSAIC_CONFIG: {
		struct vfe_cmd_demosaic_config demosaic;
			CHECKED_COPY_FROM_USER(&demosaic);

		vfe_demosaic_config(&demosaic);
	}
		break;

	case VFE_CMD_ID_FOV_CROP_CONFIG:
	case VFE_CMD_ID_FOV_CROP_UPDATE: {
		struct vfe_cmd_fov_crop_config fov;
			CHECKED_COPY_FROM_USER(&fov);

		vfe_fov_crop_config(&fov);
	}
		break;

	case VFE_CMD_ID_MAIN_SCALER_CONFIG:
	case VFE_CMD_ID_MAIN_SCALER_UPDATE: {
		struct vfe_cmd_main_scaler_config mainds;
			CHECKED_COPY_FROM_USER(&mainds);

		vfe_main_scaler_config(&mainds);
	}
		break;

	case VFE_CMD_ID_WHITE_BALANCE_CONFIG:
	case VFE_CMD_ID_WHITE_BALANCE_UPDATE: {
		struct vfe_cmd_white_balance_config wb;
			CHECKED_COPY_FROM_USER(&wb);

		vfe_white_balance_config(&wb);
	}
		break;

	case VFE_CMD_ID_COLOR_CORRECTION_CONFIG:
	case VFE_CMD_ID_COLOR_CORRECTION_UPDATE: {
		struct vfe_cmd_color_correction_config cc;
			CHECKED_COPY_FROM_USER(&cc);

		vfe_color_correction_config(&cc);
	}
		break;

	case VFE_CMD_ID_LA_CONFIG: {
		struct vfe_cmd_la_config la;
			CHECKED_COPY_FROM_USER(&la);

		vfe_la_config(&la);
	}
		break;

	case VFE_CMD_ID_RGB_GAMMA_CONFIG: {
		struct vfe_cmd_rgb_gamma_config rgb;
			CHECKED_COPY_FROM_USER(&rgb);

		rc = vfe_rgb_gamma_config(&rgb);
	}
		break;

	case VFE_CMD_ID_CHROMA_ENHAN_CONFIG:
	case VFE_CMD_ID_CHROMA_ENHAN_UPDATE: {
		struct vfe_cmd_chroma_enhan_config chrom;
			CHECKED_COPY_FROM_USER(&chrom);

		vfe_chroma_enhan_config(&chrom);
	}
		break;

	case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG:
	case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE: {
		struct vfe_cmd_chroma_suppression_config chromsup;
			CHECKED_COPY_FROM_USER(&chromsup);

		vfe_chroma_sup_config(&chromsup);
	}
		break;

	case VFE_CMD_ID_ASF_CONFIG: {
		struct vfe_cmd_asf_config asf;
			CHECKED_COPY_FROM_USER(&asf);

		vfe_asf_config(&asf);
	}
		break;

	case VFE_CMD_ID_SCALER2Y_CONFIG:
	case VFE_CMD_ID_SCALER2Y_UPDATE: {
		struct vfe_cmd_scaler2_config ds2y;
			CHECKED_COPY_FROM_USER(&ds2y);

		vfe_scaler2y_config(&ds2y);
	}
		break;

	case VFE_CMD_ID_SCALER2CbCr_CONFIG:
	case VFE_CMD_ID_SCALER2CbCr_UPDATE: {
		struct vfe_cmd_scaler2_config ds2cbcr;
			CHECKED_COPY_FROM_USER(&ds2cbcr);

		vfe_scaler2cbcr_config(&ds2cbcr);
	}
		break;

	case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: {
		struct vfe_cmd_chroma_subsample_config sub;
			CHECKED_COPY_FROM_USER(&sub);

		vfe_chroma_subsample_config(&sub);
	}
		break;

	case VFE_CMD_ID_FRAME_SKIP_CONFIG: {
		struct vfe_cmd_frame_skip_config fskip;
			CHECKED_COPY_FROM_USER(&fskip);

		vfe_frame_skip_config(&fskip);
	}
		break;

	case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: {
		struct vfe_cmd_output_clamp_config clamp;
			CHECKED_COPY_FROM_USER(&clamp);

		vfe_output_clamp_config(&clamp);
	}
		break;

	/* module update commands */
	case VFE_CMD_ID_BLACK_LEVEL_UPDATE: {
		struct vfe_cmd_black_level_config blk;
			CHECKED_COPY_FROM_USER(&blk);

		vfe_black_level_update(&blk);
	}
		break;

	case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: {
		struct vfe_cmd_demux_channel_gain_config dmu;
			CHECKED_COPY_FROM_USER(&dmu);

		vfe_demux_channel_gain_update(&dmu);
	}
		break;

	case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: {
		struct vfe_cmd_demosaic_bpc_update demo_bpc;
			CHECKED_COPY_FROM_USER(&demo_bpc);

		vfe_demosaic_bpc_update(&demo_bpc);
	}
		break;

	case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: {
		struct vfe_cmd_demosaic_abf_update demo_abf;
			CHECKED_COPY_FROM_USER(&demo_abf);

		vfe_demosaic_abf_update(&demo_abf);
	}
		break;

	case VFE_CMD_ID_LA_UPDATE: {
		struct vfe_cmd_la_config la;
			CHECKED_COPY_FROM_USER(&la);

		vfe_la_update(&la);
	}
		break;

	case VFE_CMD_ID_RGB_GAMMA_UPDATE: {
		struct vfe_cmd_rgb_gamma_config rgb;
			CHECKED_COPY_FROM_USER(&rgb);

		rc = vfe_rgb_gamma_update(&rgb);
	}
		break;

	case VFE_CMD_ID_ASF_UPDATE: {
		struct vfe_cmd_asf_update asf;
			CHECKED_COPY_FROM_USER(&asf);

		vfe_asf_update(&asf);
	}
		break;

	case VFE_CMD_ID_FRAME_SKIP_UPDATE: {
		struct vfe_cmd_frame_skip_update fskip;
			CHECKED_COPY_FROM_USER(&fskip);
			/* Start recording */
			if (fskip.output2Pattern == 0xffffffff)
				update_axi_qos(MSM_AXI_QOS_RECORDING);
			 else if (fskip.output2Pattern == 0)
				update_axi_qos(MSM_AXI_QOS_PREVIEW);

		vfe_frame_skip_update(&fskip);
	}
		break;

	case VFE_CMD_ID_CAMIF_FRAME_UPDATE: {
		struct vfe_cmds_camif_frame fup;
			CHECKED_COPY_FROM_USER(&fup);

		vfe_camif_frame_update(&fup);
	}
		break;

	/* stats update commands */
	case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: {
		struct vfe_cmd_stats_af_update afup;
			CHECKED_COPY_FROM_USER(&afup);

		vfe_stats_update_af(&afup);
	}
		break;

	case VFE_CMD_ID_STATS_WB_EXP_UPDATE: {
		struct vfe_cmd_stats_wb_exp_update wbexp;
			CHECKED_COPY_FROM_USER(&wbexp);

		vfe_stats_update_wb_exp(&wbexp);
	}
		break;

	/* control of start, stop, update, etc... */
	case VFE_CMD_ID_STOP:
		vfe_stop();
		break;

	case VFE_CMD_ID_GET_HW_VERSION:
		break;

	/* stats */
	case VFE_CMD_ID_STATS_SETTING: {
		struct vfe_cmd_stats_setting stats;
			CHECKED_COPY_FROM_USER(&stats);

		vfe_stats_setting(&stats);
	}
		break;

	case VFE_CMD_ID_STATS_AUTOFOCUS_START: {
		struct vfe_cmd_stats_af_start af;
			CHECKED_COPY_FROM_USER(&af);

		vfe_stats_start_af(&af);
	}
		break;

	case VFE_CMD_ID_STATS_AUTOFOCUS_STOP:
		vfe_stats_af_stop();
		break;

	case VFE_CMD_ID_STATS_WB_EXP_START: {
		struct vfe_cmd_stats_wb_exp_start awexp;
			CHECKED_COPY_FROM_USER(&awexp);

		vfe_stats_start_wb_exp(&awexp);
	}
		break;

	case VFE_CMD_ID_STATS_WB_EXP_STOP:
		vfe_stats_wb_exp_stop();
		break;

	case VFE_CMD_ID_ASYNC_TIMER_SETTING:
		break;

	case VFE_CMD_ID_UPDATE:
		vfe_update();
		break;

	/* test gen */
	case VFE_CMD_ID_TEST_GEN_START:
		break;

/*
  acknowledge from upper layer
	these are not in general command.

	case VFE_CMD_ID_OUTPUT1_ACK:
		break;
	case VFE_CMD_ID_OUTPUT2_ACK:
		break;
	case VFE_CMD_ID_EPOCH1_ACK:
		break;
	case VFE_CMD_ID_EPOCH2_ACK:
		break;
	case VFE_CMD_ID_STATS_AUTOFOCUS_ACK:
		break;
	case VFE_CMD_ID_STATS_WB_EXP_ACK:
		break;
*/

	default:
		pr_err("%s: invalid cmd id %d\n", __func__, cmd->id);
		rc = -EINVAL;
		break;
	} /* switch */

	return rc;
}

static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data)
{
	struct msm_pmem_region *regptr;
	struct msm_vfe_command_8k vfecmd;
	struct vfe_cmd_axi_output_config axio;
	struct axidata *axid = data;

	int rc = 0;


	if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
		cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
		cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {

		if (copy_from_user(&vfecmd,
			(void __user *)(cmd->value), sizeof(vfecmd))) {
			pr_err("%s %d: copy_from_user failed\n",
				__func__, __LINE__);
			return -EFAULT;
		}
	}

	CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);

	switch (cmd->cmd_type) {
	case CMD_GENERAL:
		rc = vfe_proc_general(&vfecmd);
		break;

	case CMD_STATS_ENABLE:
	case CMD_STATS_AXI_CFG: {
			int i;
			struct vfe_cmd_stats_setting scfg;

			BUG_ON(!axid);

			if (vfecmd.length != sizeof(scfg)) {
				pr_err
				("msm_camera: %s: cmd %d: user-space "\
				"data size %d != kernel data size %d\n",
				__func__,
				cmd->cmd_type, vfecmd.length,
				sizeof(scfg));
				return -EIO;
			}

			if (copy_from_user(&scfg,
				(void __user *)(vfecmd.value),
				sizeof(scfg))) {
				pr_err("%s %d: copy_from_user failed\n",
				__func__, __LINE__);
			return -EFAULT;
		}

		regptr = axid->region;
		if (axid->bufnum1 > 0) {
			for (i = 0; i < axid->bufnum1; i++) {
					scfg.awbBuffer[i] =
					(uint32_t)(regptr->paddr);
				regptr++;
			}
		}

		if (axid->bufnum2 > 0) {
			for (i = 0; i < axid->bufnum2; i++) {
					scfg.afBuffer[i] =
					(uint32_t)(regptr->paddr);
				regptr++;
			}
		}

			vfe_stats_setting(&scfg);
	}
		break;

	case CMD_STATS_AF_AXI_CFG:
		break;

	case CMD_FRAME_BUF_RELEASE: {
		/* preview buffer release */
		struct msm_frame *b;
		unsigned long p;
		struct vfe_cmd_output_ack fack;

			BUG_ON(!data);

		b = (struct msm_frame *)(cmd->value);
		p = *(unsigned long *)data;

			fack.ybufaddr[0] = (uint32_t) (p + b->planar0_off);

			fack.chromabufaddr[0] = (uint32_t) (p + b->planar1_off);

		if (b->path == OUTPUT_TYPE_P)
			vfe_output_p_ack(&fack);

		if ((b->path == OUTPUT_TYPE_V)
			 || (b->path == OUTPUT_TYPE_S))
			vfe_output_v_ack(&fack);
	}
		break;

	case CMD_SNAP_BUF_RELEASE:
		break;

	case CMD_STATS_BUF_RELEASE: {
		struct vfe_cmd_stats_wb_exp_ack sack;

			BUG_ON(!data);

		sack.nextWbExpOutputBufferAddr = *(uint32_t *)data;
		vfe_stats_wb_exp_ack(&sack);
	}
		break;

	case CMD_STATS_AF_BUF_RELEASE: {
		struct vfe_cmd_stats_af_ack ack;

			BUG_ON(!data);

		ack.nextAFOutputBufferAddr = *(uint32_t *)data;
		vfe_stats_af_ack(&ack);
	}
		break;

	case CMD_AXI_CFG_PREVIEW:
	case CMD_RAW_PICT_AXI_CFG: {

			BUG_ON(!axid);

			if (copy_from_user(&axio, (void __user *)(vfecmd.value),
				sizeof(axio))) {
				pr_err("%s %d: copy_from_user failed\n",
					__func__, __LINE__);
			return -EFAULT;
		}
			/* Validate the data from user space */
			if (axio.output2.fragmentCount <
				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
				axio.output2.fragmentCount >
				VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
				return -EINVAL;

			vfe_config_axi(OUTPUT_2, axid, &axio);
			axio.outputDataSize = 0;
			vfe_axi_output_config(&axio);
	}
		break;

	case CMD_AXI_CFG_SNAP: {

			BUG_ON(!axid);

			if (copy_from_user(&axio, (void __user *)(vfecmd.value),
				sizeof(axio))) {
				pr_err("%s %d: copy_from_user failed\n",
					__func__, __LINE__);
			return -EFAULT;
		}
			/* Validate the data from user space */
			if (axio.output1.fragmentCount <
				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
				axio.output1.fragmentCount >
				VFE_MAX_NUM_FRAGMENTS_PER_FRAME ||
				axio.output2.fragmentCount <
				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
				axio.output2.fragmentCount >
				VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
				return -EINVAL;

			vfe_config_axi(OUTPUT_1_AND_2, axid, &axio);
			vfe_axi_output_config(&axio);
	}
		break;

	case CMD_AXI_CFG_VIDEO: {
			BUG_ON(!axid);

			if (copy_from_user(&axio, (void __user *)(vfecmd.value),
				sizeof(axio))) {
				pr_err("%s %d: copy_from_user failed\n",
					__func__, __LINE__);
			return -EFAULT;
		}
			/* Validate the data from user space */
			if (axio.output1.fragmentCount <
				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
				axio.output1.fragmentCount >
				VFE_MAX_NUM_FRAGMENTS_PER_FRAME ||
				axio.output2.fragmentCount <
				VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
				axio.output2.fragmentCount >
				VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
				return -EINVAL;

			vfe_config_axi(OUTPUT_1_AND_3, axid, &axio);
			axio.outputDataSize = 0;
			vfe_axi_output_config(&axio);
	}
		break;

	default:
		break;
	} /* switch */

	return rc;
}

static int vfe_init(struct msm_vfe_callback *presp, struct platform_device *dev)
{
	int rc = 0;

	rc = vfe_cmd_init(presp, dev, vfe_syncdata);
	if (rc < 0)
		return rc;

	/* Bring up all the required GPIOs and Clocks */
	rc = msm_camio_enable(dev);

	return rc;
}

void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
{
	fptr->vfe_init    = vfe_init;
	fptr->vfe_enable  = vfe_enable;
	fptr->vfe_config  = vfe_config;
	fptr->vfe_disable = vfe_disable;
	fptr->vfe_release = vfe_release;
	vfe_syncdata = data;
}

void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
{
	fptr->vpe_reg		= NULL;
	fptr->send_frame_to_vpe	= NULL;
	fptr->vpe_config	= NULL;
	fptr->vpe_cfg_update	= NULL;
	fptr->dis		= NULL;
}
