/* Copyright (c) 2011, The Linux Foundation. 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/delay.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include "mt9d113.h"

/* Micron MT9D113 Registers and their values */
#define  REG_MT9D113_MODEL_ID	0x0000
#define  MT9D113_MODEL_ID		0x2580
#define Q8						0x00000100

struct mt9d113_work {
	struct work_struct work;
};

static struct  mt9d113_work *mt9d113_sensorw;
static struct  i2c_client *mt9d113_client;

struct mt9d113_ctrl {
	const struct msm_camera_sensor_info *sensordata;
	uint32_t sensormode;
	uint32_t fps_divider;/* init to 1 * 0x00000400 */
	uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
	uint16_t fps;
	uint16_t curr_step_pos;
	uint16_t my_reg_gain;
	uint32_t my_reg_line_count;
	uint16_t total_lines_per_frame;
	uint16_t config_csi;
	enum mt9d113_resolution_t prev_res;
	enum mt9d113_resolution_t pict_res;
	enum mt9d113_resolution_t curr_res;
	enum mt9d113_test_mode_t  set_test;
};

static struct mt9d113_ctrl *mt9d113_ctrl;

static DECLARE_WAIT_QUEUE_HEAD(mt9d113_wait_queue);
DEFINE_MUTEX(mt9d113_mut);

static int mt9d113_i2c_rxdata(unsigned short saddr,
				unsigned char *rxdata, int length)
{
	struct i2c_msg msgs[] = {
		{
			.addr   = saddr,
			.flags = 0,
			.len   = 2,
			.buf   = rxdata,
		},
		{
			.addr   = saddr,
			.flags = I2C_M_RD,
			.len   = length,
			.buf   = rxdata,
		},
	};
	if (i2c_transfer(mt9d113_client->adapter, msgs, 2) < 0) {
		CDBG("mt9d113_i2c_rxdata failed!\n");
		return -EIO;
	}
	return 0;
}

static int32_t mt9d113_i2c_read(unsigned short   saddr,
				unsigned short raddr,
				unsigned short *rdata,
				enum mt9d113_width width)
{
	int32_t rc = 0;
	unsigned char buf[4];
	if (!rdata)
		return -EIO;
	memset(buf, 0, sizeof(buf));
	switch (width) {
	case WORD_LEN: {
			buf[0] = (raddr & 0xFF00)>>8;
			buf[1] = (raddr & 0x00FF);
			rc = mt9d113_i2c_rxdata(saddr, buf, 2);
			if (rc < 0)
				return rc;
			*rdata = buf[0] << 8 | buf[1];
		}
		break;
	default:
		break;
	}
	if (rc < 0)
		CDBG("mt9d113_i2c_read failed !\n");
	return rc;
}

static int32_t mt9d113_i2c_txdata(unsigned short saddr,
				unsigned char *txdata, int length)
{
	struct i2c_msg msg[] = {
		{
			.addr = saddr,
			.flags = 0,
			.len = length,
			.buf = txdata,
		},
	};
	if (i2c_transfer(mt9d113_client->adapter, msg, 1) < 0) {
		CDBG("mt9d113_i2c_txdata failed\n");
		return -EIO;
	}
	return 0;
}

static int32_t mt9d113_i2c_write(unsigned short saddr,
				unsigned short waddr,
				unsigned short wdata,
				enum mt9d113_width width)
{
	int32_t rc = -EIO;
	unsigned char buf[4];
	memset(buf, 0, sizeof(buf));
	switch (width) {
	case WORD_LEN: {
			buf[0] = (waddr & 0xFF00)>>8;
			buf[1] = (waddr & 0x00FF);
			buf[2] = (wdata & 0xFF00)>>8;
			buf[3] = (wdata & 0x00FF);
			rc = mt9d113_i2c_txdata(saddr, buf, 4);
		}
		break;
	case BYTE_LEN: {
			buf[0] = waddr;
			buf[1] = wdata;
			rc = mt9d113_i2c_txdata(saddr, buf, 2);
		}
		break;
	default:
		break;
	}
	if (rc < 0)
		printk(KERN_ERR
			"i2c_write failed, addr = 0x%x, val = 0x%x!\n",
			waddr, wdata);
	return rc;
}

static int32_t mt9d113_i2c_write_table(
				struct mt9d113_i2c_reg_conf
				const *reg_conf_tbl,
				int num_of_items_in_table)
{
	int i;
	int32_t rc = -EIO;
	for (i = 0; i < num_of_items_in_table; i++) {
		rc = mt9d113_i2c_write(mt9d113_client->addr,
				reg_conf_tbl->waddr, reg_conf_tbl->wdata,
				WORD_LEN);
		if (rc < 0)
			break;
		reg_conf_tbl++;
	}
	return rc;
}

static long mt9d113_reg_init(void)
{
	uint16_t data = 0;
	int32_t rc = 0;
	int count = 0;
	struct msm_camera_csi_params mt9d113_csi_params;
	if (!mt9d113_ctrl->config_csi) {
		mt9d113_csi_params.lane_cnt = 1;
		mt9d113_csi_params.data_format = CSI_8BIT;
		mt9d113_csi_params.lane_assign = 0xe4;
		mt9d113_csi_params.dpcm_scheme = 0;
		mt9d113_csi_params.settle_cnt = 0x14;
		rc = msm_camio_csi_config(&mt9d113_csi_params);
		mt9d113_ctrl->config_csi = 1;
		msleep(50);
	}
	/* Disable parallel and enable mipi*/
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x001A,
				0x0051, WORD_LEN);
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x001A,
				0x0050,
				WORD_LEN);
	msleep(20);
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x001A,
				0x0058,
				WORD_LEN);

	/* Preset pll settings begin*/
	rc = mt9d113_i2c_write_table(&mt9d113_regs.pll_tbl[0],
				mt9d113_regs.pll_tbl_size);
	if (rc < 0)
		return rc;
	rc = mt9d113_i2c_read(mt9d113_client->addr,
				0x0014, &data, WORD_LEN);
	data = data&0x8000;
	/* Poll*/
	while (data == 0x0000) {
		data = 0;
		rc = mt9d113_i2c_read(mt9d113_client->addr,
				0x0014, &data, WORD_LEN);
		data = data & 0x8000;
		usleep_range(11000, 12000);
		count++;
		if (count == 100) {
			CDBG(" Timeout:1\n");
			break;
		}
	}
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x0014,
				0x20FA,
				WORD_LEN);

	/*Preset pll Ends*/
	mt9d113_i2c_write(mt9d113_client->addr,
				0x0018,
				0x402D,
				WORD_LEN);

	mt9d113_i2c_write(mt9d113_client->addr,
				0x0018,
				0x402C,
				WORD_LEN);
	/*POLL_REG=0x0018,0x4000,!=0x0000,DELAY=10,TIMEOUT=100*/
	data = 0;
	rc = mt9d113_i2c_read(mt9d113_client->addr,
		0x0018, &data, WORD_LEN);
	data = data & 0x4000;
	count = 0;
	while (data != 0x0000) {
		rc = mt9d113_i2c_read(mt9d113_client->addr,
			0x0018, &data, WORD_LEN);
		data = data & 0x4000;
		CDBG(" data is %d\n" , data);
		usleep_range(11000, 12000);
		count++;
		if (count == 100) {
			CDBG(" Loop2 timeout: MT9D113\n");
			break;
		}
		CDBG(" Not streaming\n");
	}
	CDBG("MT9D113: Start stream\n");
	/*Preset Register Wizard Conf*/
	rc = mt9d113_i2c_write_table(&mt9d113_regs.register_tbl[0],
				mt9d113_regs.register_tbl_size);
	if (rc < 0)
		return rc;
	rc = mt9d113_i2c_write_table(&mt9d113_regs.err_tbl[0],
				mt9d113_regs.err_tbl_size);
	if (rc < 0)
		return rc;
	rc = mt9d113_i2c_write_table(&mt9d113_regs.eeprom_tbl[0],
				mt9d113_regs.eeprom_tbl_size);
	if (rc < 0)
		return rc;

	rc = mt9d113_i2c_write_table(&mt9d113_regs.low_light_tbl[0],
				mt9d113_regs.low_light_tbl_size);
	if (rc < 0)
		return rc;

	rc = mt9d113_i2c_write_table(&mt9d113_regs.awb_tbl[0],
				mt9d113_regs.awb_tbl_size);
	if (rc < 0)
		return rc;

	rc = mt9d113_i2c_write_table(&mt9d113_regs.patch_tbl[0],
				mt9d113_regs.patch_tbl_size);
	if (rc < 0)
		return rc;

	/*check patch load*/
	mt9d113_i2c_write(mt9d113_client->addr,
				0x098C,
				0xA024,
				WORD_LEN);
	count = 0;
	/*To check if patch is loaded properly
	poll the register 0x990 till the condition is
	met or till the timeout*/
	data = 0;
	rc = mt9d113_i2c_read(mt9d113_client->addr,
				0x0990, &data, WORD_LEN);
	while (data == 0) {
		data = 0;
		rc = mt9d113_i2c_read(mt9d113_client->addr,
				0x0990, &data, WORD_LEN);
		usleep_range(11000, 12000);
		count++;
		if (count == 100) {
			CDBG("Timeout in patch loading\n");
			break;
		}
	}
		/*BITFIELD=0x0018, 0x0004, 0*/
	/*Preset continue begin */
	rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0018, 0x0028,
				WORD_LEN);
	CDBG(" mt9d113 wait for seq done\n");
	/* syncronize the FW with the sensor
	MCU_ADDRESS [SEQ_CMD]*/
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x098C, 0xA103, WORD_LEN);
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x0990, 0x0006, WORD_LEN);
		/*mt9d113 wait for seq done
	 syncronize the FW with the sensor */
	msleep(20);
	/*Preset continue end */
	CDBG(" MT9D113: Preset continue end\n");
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x0012,
				0x00F5,
				WORD_LEN);
	/*continue begin */
	CDBG(" MT9D113: Preset continue begin\n");
	rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0018, 0x0028 ,
				WORD_LEN);
	/*mt9d113 wait for seq done
	 syncronize the FW with the sensor
	MCU_ADDRESS [SEQ_CMD]*/
	msleep(20);
	rc = mt9d113_i2c_write(mt9d113_client->addr,
				0x098C, 0xA103, WORD_LEN);
	/* MCU DATA */
	rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0990,
				0x0006, WORD_LEN);
	/*mt9d113 wait for seq done
	syncronize the FW with the sensor */
	/* MCU_ADDRESS [SEQ_CMD]*/
	msleep(20);
	/*Preset continue end*/
	return rc;

}

static long mt9d113_set_sensor_mode(int mode)
{
	long rc = 0;
	switch (mode) {
	case SENSOR_PREVIEW_MODE:
		rc = mt9d113_reg_init();
		CDBG("MT9D113: configure to preview begin\n");
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x098C, 0xA115, WORD_LEN);
		if (rc < 0)
			return rc;
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x0990, 0x0000, WORD_LEN);
		if (rc < 0)
			return rc;
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x098C, 0xA103, WORD_LEN);
		if (rc < 0)
			return rc;
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x0990, 0x0001, WORD_LEN);
		if (rc < 0)
			return rc;
		break;
	case SENSOR_SNAPSHOT_MODE:
	case SENSOR_RAW_SNAPSHOT_MODE:
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x098C, 0xA115, WORD_LEN);
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x0990, 0x0002, WORD_LEN);
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x098C, 0xA103, WORD_LEN);
		rc =
		mt9d113_i2c_write(mt9d113_client->addr,
						0x0990, 0x0002, WORD_LEN);
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static int mt9d113_sensor_init_probe(const struct
				msm_camera_sensor_info * data)
{
	uint16_t model_id = 0;
	int rc = 0;
	/* Read the Model ID of the sensor */
	rc = mt9d113_i2c_read(mt9d113_client->addr,
						REG_MT9D113_MODEL_ID,
						&model_id, WORD_LEN);
	if (rc < 0)
		goto init_probe_fail;
	/* Check if it matches it with the value in Datasheet */
	if (model_id != MT9D113_MODEL_ID)
		printk(KERN_INFO "mt9d113 model_id = 0x%x\n", model_id);
	if (rc < 0)
		goto init_probe_fail;
	return rc;
init_probe_fail:
	printk(KERN_INFO "probe fail\n");
	return rc;
}

static int mt9d113_init_client(struct i2c_client *client)
{
	/* Initialize the MSM_CAMI2C Chip */
	init_waitqueue_head(&mt9d113_wait_queue);
	return 0;
}

int mt9d113_sensor_config(void __user *argp)
{
	struct sensor_cfg_data cfg_data;
	long rc = 0;

	if (copy_from_user(&cfg_data,
					(void *)argp,
					(sizeof(struct sensor_cfg_data))))
		return -EFAULT;
	mutex_lock(&mt9d113_mut);
	CDBG("mt9d113_ioctl, cfgtype = %d, mode = %d\n",
		 cfg_data.cfgtype, cfg_data.mode);
	switch (cfg_data.cfgtype) {
	case CFG_SET_MODE:
		rc = mt9d113_set_sensor_mode(
						cfg_data.mode);
		break;
	case CFG_SET_EFFECT:
		return rc;
	case CFG_GET_AF_MAX_STEPS:
	default:
		rc = -EINVAL;
		break;
	}
	mutex_unlock(&mt9d113_mut);
	return rc;
}

int mt9d113_sensor_release(void)
{
	int rc = 0;

	mutex_lock(&mt9d113_mut);
	gpio_set_value_cansleep(mt9d113_ctrl->sensordata->sensor_reset, 0);
	msleep(20);
	gpio_free(mt9d113_ctrl->sensordata->sensor_reset);
	kfree(mt9d113_ctrl);
	mutex_unlock(&mt9d113_mut);

	return rc;
}

static int mt9d113_probe_init_done(const struct msm_camera_sensor_info
				*data)
{
	gpio_free(data->sensor_reset);
	return 0;
}

static int mt9d113_probe_init_sensor(const struct msm_camera_sensor_info
				*data)
{
	int32_t rc = 0;
	uint16_t chipid = 0;

	rc = gpio_request(data->sensor_reset, "mt9d113");
	printk(KERN_INFO " mt9d113_probe_init_sensor\n");
	if (!rc) {
		printk(KERN_INFO "sensor_reset = %d\n", rc);
		gpio_direction_output(data->sensor_reset, 0);
		usleep_range(11000, 12000);
		gpio_set_value_cansleep(data->sensor_reset, 1);
		usleep_range(11000, 12000);
	} else
		goto init_probe_done;
	printk(KERN_INFO " mt9d113_probe_init_sensor called\n");
	rc = mt9d113_i2c_read(mt9d113_client->addr, REG_MT9D113_MODEL_ID,
						&chipid, WORD_LEN);
	if (rc < 0)
		goto init_probe_fail;
	/*Compare sensor ID to MT9D113 ID: */
	if (chipid != MT9D113_MODEL_ID) {
		printk(KERN_INFO "mt9d113_probe_init_sensor chip id is%d\n",
			chipid);
	}
	CDBG("mt9d113_probe_init_sensor Success\n");
	goto init_probe_done;
init_probe_fail:
	CDBG(" ov2720_probe_init_sensor fails\n");
	gpio_set_value_cansleep(data->sensor_reset, 0);
	mt9d113_probe_init_done(data);
init_probe_done:
	printk(KERN_INFO " mt9d113_probe_init_sensor finishes\n");
	return rc;
}

static int mt9d113_i2c_probe(struct i2c_client *client,
				const struct i2c_device_id *id)
{
	int rc = 0;
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		rc = -ENOTSUPP;
		goto probe_failure;
	}
	mt9d113_sensorw =
	kzalloc(sizeof(struct mt9d113_work), GFP_KERNEL);
	if (!mt9d113_sensorw) {
		rc = -ENOMEM;
		goto probe_failure;
	}
	i2c_set_clientdata(client, mt9d113_sensorw);
	mt9d113_init_client(client);
	mt9d113_client = client;
	CDBG("mt9d113_probe succeeded!\n");
	return 0;
probe_failure:
	kfree(mt9d113_sensorw);
	mt9d113_sensorw = NULL;
	CDBG("mt9d113_probe failed!\n");
	return rc;
}

static const struct i2c_device_id mt9d113_i2c_id[] = {
	{ "mt9d113", 0},
	{},
};

static struct i2c_driver mt9d113_i2c_driver = {
	.id_table = mt9d113_i2c_id,
	.probe  = mt9d113_i2c_probe,
	.remove = __exit_p(mt9d113_i2c_remove),
			  .driver = {
		.name = "mt9d113",
	},
};

int mt9d113_sensor_open_init(const struct msm_camera_sensor_info *data)
{
	int32_t rc = 0;
	mt9d113_ctrl = kzalloc(sizeof(struct mt9d113_ctrl), GFP_KERNEL);
	if (!mt9d113_ctrl) {
		printk(KERN_INFO "mt9d113_init failed!\n");
		rc = -ENOMEM;
		goto init_done;
	}
	mt9d113_ctrl->fps_divider = 1 * 0x00000400;
	mt9d113_ctrl->pict_fps_divider = 1 * 0x00000400;
	mt9d113_ctrl->set_test = TEST_OFF;
	mt9d113_ctrl->config_csi = 0;
	mt9d113_ctrl->prev_res = QTR_SIZE;
	mt9d113_ctrl->pict_res = FULL_SIZE;
	mt9d113_ctrl->curr_res = INVALID_SIZE;
	if (data)
		mt9d113_ctrl->sensordata = data;
	if (rc < 0) {
		printk(KERN_INFO "mt9d113_sensor_open_init fail\n");
		return rc;
	}
		/* enable mclk first */
		msm_camio_clk_rate_set(24000000);
		msleep(20);
		rc = mt9d113_probe_init_sensor(data);
		if (rc < 0)
			goto init_fail;
		mt9d113_ctrl->fps = 30*Q8;
		rc = mt9d113_sensor_init_probe(data);
		if (rc < 0) {
			gpio_set_value_cansleep(data->sensor_reset, 0);
			goto init_fail;
		} else
			printk(KERN_ERR "%s: %d\n", __func__, __LINE__);
		goto init_done;
init_fail:
		printk(KERN_INFO "init_fail\n");
		mt9d113_probe_init_done(data);
init_done:
		CDBG("init_done\n");
		return rc;
}

static int mt9d113_sensor_probe(const struct msm_camera_sensor_info
				*info,
				struct msm_sensor_ctrl *s)
{
	int rc = 0;
	rc = i2c_add_driver(&mt9d113_i2c_driver);
	if (rc < 0 || mt9d113_client == NULL) {
		rc = -ENOTSUPP;
		goto probe_fail;
	}
	msm_camio_clk_rate_set(24000000);
	usleep_range(5000, 6000);
	rc = mt9d113_probe_init_sensor(info);
	if (rc < 0)
		goto probe_fail;
	s->s_init = mt9d113_sensor_open_init;
	s->s_release = mt9d113_sensor_release;
	s->s_config  = mt9d113_sensor_config;
	s->s_camera_type = FRONT_CAMERA_2D;
	s->s_mount_angle  = 0;
	gpio_set_value_cansleep(info->sensor_reset, 0);
	mt9d113_probe_init_done(info);
	return rc;
probe_fail:
	printk(KERN_INFO "mt9d113_sensor_probe: SENSOR PROBE FAILS!\n");
	return rc;
}

static int __mt9d113_probe(struct platform_device *pdev)
{
	return msm_camera_drv_start(pdev, mt9d113_sensor_probe);
}

static struct platform_driver msm_camera_driver = {
	.probe = __mt9d113_probe,
	.driver = {
		.name = "msm_cam_mt9d113",
		.owner = THIS_MODULE,
	},
};

static int __init mt9d113_init(void)
{
	return platform_driver_register(&msm_camera_driver);
}

module_init(mt9d113_init);

MODULE_DESCRIPTION("Micron 2MP YUV sensor driver");
MODULE_LICENSE("GPL v2");
