// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * drivers/amlogic/media/camera/common/cam_prober.c
 *
 * Copyright (C) 2017 Amlogic, Inc. 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 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.
 */

#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include <linux/delay.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin.h>
#include <linux/amlogic/media/camera/aml_cam_info.h>
#include <linux/amlogic/aml_gpio_consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/cpu_version.h>
//#include <linux/amlogic/media/old_cpu_version.h>
#include <linux/amlogic/media/utils/am_com.h>
#include <linux/clk.h>
#include <linux/errno.h>

#define CONFIG_ARCH_MESON8

static struct platform_device *cam_pdev;
static struct clk *cam_clk;
static unsigned int bt_path_count;

static unsigned int camera0_pwdn_pin;
static unsigned int camera0_rst_pin;
static unsigned int camera1_pwdn_pin;
static unsigned int camera1_rst_pin;

static struct aml_cam_info_s *temp_cam;

static int aml_camera_read_buff(struct i2c_adapter *adapter,
				unsigned short dev_addr, char *buf,
				int addr_len, int data_len)
{
	int i2c_flag = -1;
	struct i2c_msg msgs[] = {{
			.addr = dev_addr, .flags = 0,
		    .len = addr_len, .buf = buf,
		}, {
			.addr = dev_addr, .flags = I2C_M_RD,
		    .len = data_len, .buf = buf,
		}
	};

	i2c_flag = i2c_transfer(adapter, msgs, 2);

	return i2c_flag;
}

struct aml_cam_info_s *get_aml_cam_info_s(void)
{
	return temp_cam;
}

static int aml_camera_write_buff(struct i2c_adapter *adapter,
				 unsigned short dev_addr, char *buf, int len)
{
	struct i2c_msg msg[] = {{
			.addr = dev_addr, .flags = 0,    /*I2C_M_TEN*/
			.len = len, .buf = buf,
		}
	};

	if (i2c_transfer(adapter, msg, 1) < 0)
		return -1;
	else
		return 0;
}

static int aml_i2c_get_byte(struct i2c_adapter *adapter,
			    unsigned short dev_addr, unsigned short addr)
{
	unsigned char buff[4];

	buff[0] = (unsigned char)((addr >> 8) & 0xff);
	buff[1] = (unsigned char)(addr & 0xff);

	if (aml_camera_read_buff(adapter, dev_addr, buff, 2, 1) < 0)
		return -1;
	return buff[0];
}

static int aml_i2c_put_byte(struct i2c_adapter *adapter,
			    unsigned short dev_addr, unsigned short addr,
			    unsigned char data)
{
	unsigned char buff[4];

	buff[0] = (unsigned char)((addr >> 8) & 0xff);
	buff[1] = (unsigned char)(addr & 0xff);
	buff[2] = data;
	if (aml_camera_write_buff(adapter, dev_addr, buff, 3) < 0)
		return -1;
	return 0;
}

static int aml_i2c_get_byte_add8(struct i2c_adapter *adapter,
				 unsigned short dev_addr,
				 unsigned short addr)
{
	unsigned char buff[4];

	buff[0] = (unsigned char)(addr & 0xff);

	if (aml_camera_read_buff(adapter, dev_addr, buff, 1, 1) < 0)
		return -1;
	return buff[0];
}

static int aml_i2c_put_byte_add8(struct i2c_adapter *adapter,
				 unsigned short dev_addr,
				 unsigned short addr, unsigned char data)
{
	unsigned char buff[4];

	buff[0] = (unsigned char)(addr & 0xff);
	buff[1] = data;
	if (aml_camera_write_buff(adapter, dev_addr, buff, 2) < 0)
		return -1;
	return 0;
}

int aml_i2c_put_word(struct i2c_adapter *adapter, unsigned short dev_addr,
		     unsigned short addr, unsigned short data)
{
	unsigned char buff[4];

	buff[0] = (unsigned char)((addr >> 8) & 0xff);
	buff[1] = (unsigned char)(addr & 0xff);
	buff[2] = (unsigned char)((data >> 8) & 0xff);
	buff[3] = (unsigned char)(data & 0xff);
	if (aml_camera_write_buff(adapter, dev_addr, buff, 4) < 0)
		return -1;
	return 0;
}

static int aml_i2c_get_word(struct i2c_adapter *adapter,
			    unsigned short dev_addr, unsigned short addr)
{
	int ret;
	unsigned char buff[4];

	buff[0] = (unsigned char)((addr >> 8) & 0xff);
	buff[1] = (unsigned char)(addr & 0xff);
	if (aml_camera_read_buff(adapter, dev_addr, buff, 2, 2) < 0)
		return -1;
	ret = (buff[0] << 8) | (buff[1]);
	return ret;
}

static int aml_i2c_get_word_add8(struct i2c_adapter *adapter,
				 unsigned short dev_addr,
				 unsigned short addr)
{
	int ret;
	unsigned char buff[4];

	buff[0] = (unsigned char)((addr >> 8) & 0xff);
	buff[1] = (unsigned char)(addr & 0xff);
	if (aml_camera_read_buff(adapter, dev_addr, buff, 2, 2) < 0)
		return -1;
	ret = buff[0] | (buff[1] << 8);
	return ret;
}

static int aml_i2c_put_word_add8(struct i2c_adapter *adapter,
				 unsigned short dev_addr, unsigned char addr,
				 unsigned short data)
{
	unsigned char buff[4];

	buff[0] = (unsigned char)(addr & 0xff);
	buff[1] = (unsigned char)(data >> 8 & 0xff);
	buff[2] = (unsigned char)(data & 0xff);
	if (aml_camera_write_buff(adapter, dev_addr, buff, 3) < 0)
		return -1;
	return 0;
}

extern struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0307
int gc0307_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x21, 0x00);
	if (reg == 0x99)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0308
int gc0308_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x21, 0x00);
	if (reg == 0x9b)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0328
int gc0328_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x21, 0xf0);
	if (reg == 0x9d)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0329
int gc0329_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	aml_i2c_put_byte_add8(adapter, 0x31, 0xfc, 0x16); /*select page 0*/
	reg = aml_i2c_get_byte_add8(adapter, 0x31, 0x00);
	if (reg == 0xc0)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2015
int gc2015_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x30, 0x00);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x30, 0x01);
	if (reg[0] == 0x20 && reg[1] == 0x05)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HM2057
int hm2057_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x24, 0x0001);
	reg[1] = aml_i2c_get_byte(adapter, 0x24, 0x0002);
	if (reg[0] == 0x20 && reg[1] == 0x56)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2035
int gc2035_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf0);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf1);
	if (reg[0] == 0x20 && reg[1] == 0x35)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2155
int gc2155_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf0);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf1);
	if (reg[0] == 0x21 && reg[1] == 0x55)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GT2005
int gt2005_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x3c, 0x0000);
	reg[1] = aml_i2c_get_byte(adapter, 0x3c, 0x0001);
	if (reg[0] == 0x51 && reg[1] == 0x38)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV2659
int ov2659_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x30, 0x300a);
	reg[1] = aml_i2c_get_byte(adapter, 0x30, 0x300b);
	if (reg[0] == 0x26 && reg[1] == 0x56)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV3640
int ov3640_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x3c, 0x300a);
	reg[1] = aml_i2c_get_byte(adapter, 0x3c, 0x300b);
	if (reg[0] == 0x36 && reg[1] == 0x4c)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV3660
int ov3660_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x3c, 0x300a);
	reg[1] = aml_i2c_get_byte(adapter, 0x3c, 0x300b);
	if (reg[0] == 0x36 && reg[1] == 0x60)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV5640
int ov5640_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x3c, 0x300a);
	reg[1] = aml_i2c_get_byte(adapter, 0x3c, 0x300b);
	if (reg[0] == 0x56 && reg[1] == 0x40) {
		ret = 1;
		ov5640_i2c_driver_init();
	}
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV5642
int ov5642_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x3c, 0x300a);
	reg[1] = aml_i2c_get_byte(adapter, 0x3c, 0x300b);
	if (reg[0] == 0x56 && reg[1] == 0x42)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV7675
int ov7675_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x21, 0x0a);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x21, 0x0b);
	if (reg[0] == 0x76 && reg[1] == 0x73)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP0A19
int sp0a19_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x21, 0x02);
	if (reg == 0xa6)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP2518
int sp2518_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x30, 0x02);
	pr_info("sp2518 chip id reg = %x .\n", reg);
	if (reg == 0x53)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP0838
int sp0838_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x18, 0x02);
	if (reg == 0x27)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HI253
int hi253_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg;

	reg = aml_i2c_get_byte_add8(adapter, 0x20, 0x04);
	if (reg == 0x92)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HM5065
int hm5065_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x1F, 0x0000);
	reg[1] = aml_i2c_get_byte(adapter, 0x1F, 0x0001);
	if (reg[0] == 0x03 && reg[1] == 0x9e)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HM1375
int hm1375_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x24, 0x0001);
	reg[1] = aml_i2c_get_byte(adapter, 0x24, 0x0002);
	pr_info("%s: device ID: 0x%x%x", __func__, reg[0], reg[1]);
	if ((reg[0] == 0x13 || reg[0] == 0x03) && reg[1] == 0x75)
		ret = 1;
	ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HI2056
int hi2056_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x24, 0x0001);
	reg[1] = aml_i2c_get_byte(adapter, 0x24, 0x0002);
	pr_info("reg[0]=%x, reg[1]=%x\n", reg[0], reg[1]);
	if (reg[0] == 0x20 && reg[1] == 0x56)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV5647
int ov5647_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte(adapter, 0x36, 0x300a);
	reg[1] = aml_i2c_get_byte(adapter, 0x36, 0x300b);
	pr_info("reg[0]:%x,reg[1]:%x\n", reg[0], reg[1]);
	if (reg[0] == 0x56 && reg[1] == 0x47)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_AR0543
int ar0543_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0, reg_val;

	reg_val = aml_i2c_get_word(adapter, 0x36, 0x3000);
	pr_info("reg:0x%x\n", reg_val);
	if (reg_val == 0x4800)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_AR0833
int ar0833_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0, reg_val;

	reg_val = aml_i2c_get_word(adapter, 0x36, 0x3000);
	pr_info("reg:0x%x\n", reg_val);
	if (reg_val == 0x4B03)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP1628
int sp1628_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x3c, 0x02);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xa0);
	if (reg[0] == 0x16 && reg[1] == 0x28)
		ret = 1;
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_BF3720
int bf3720_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x6e, 0xfc);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x6e, 0xfd);
	if (reg[0] == 0x37 && reg[1] == 0x20)
		ret = 1;
	return ret;
}
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_BF3703
int __init bf3703_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x6e, 0xfc); /*i2c addr:0x6f*/
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x6e, 0xfd);
	if (reg[0] == 0x37 && reg[1] == 0x03)
		ret = 1;
	return ret;
}
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_BF3920
int __init bf3920_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x6e, 0xfc); /*i2c addr:0x6f*/
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x6e, 0xfd);
	if (reg[0] == 0x39 && reg[1] == 0x20)
		ret = 1;
	return ret;
}
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2145
int __init gc2145_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf0);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf1);
	/*datasheet chip id is error*/
	if (reg[0] == 0x21 && reg[1] == 0x45)
		ret = 1;
	pr_info("%s, ret = %d\n", __func__, ret);
	return ret;
}
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2145_MIPI
int __init gc2145_mipi_v4l2_probe(struct i2c_adapter *adapter)
{
	int ret = 0;
	unsigned char reg[2];

	reg[0] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf0);
	reg[1] = aml_i2c_get_byte_add8(adapter, 0x3c, 0xf1);
	/*datasheet chip id is error*/
	if (reg[0] == 0x21 && reg[1] == 0x45) {
		ret = 1;
		gc2145_mipi_i2c_driver_init();
	}
	pr_info("%s, ret = %d\n", __func__, ret);
	return ret;
}
#endif

struct aml_cam_dev_info_s {
	unsigned char addr;
	char *name;
	unsigned char pwdn;
	enum resolution_size max_cap_size;
	aml_cam_probe_fun_t probe_func;
};

static const struct aml_cam_dev_info_s cam_devs[] = {
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0307
	{
		.addr = 0x21,
		.name = "gc0307",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = gc0307_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0308
	{
		.addr = 0x21,
		.name = "gc0308",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = gc0308_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0328
	{
		.addr = 0x21,
		.name = "gc0328",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = gc0328_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC0329
	{
		.addr = 0x31,
		.name = "gc0329",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = gc0329_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2015
	{
		.addr = 0x30,
		.name = "gc2015",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = gc2015_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HM2057
	{
		.addr = 0x24,
		.name = "hm2057",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = hm2057_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2035
	{
		.addr = 0x3c,
		.name = "gc2035",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = gc2035_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2155
	{
		.addr = 0x3c,
		.name = "gc2155",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = gc2155_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GT2005
	{
		.addr = 0x3c,
		.name = "gt2005",
		.pwdn = 0,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = gt2005_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV2659
	{
		.addr = 0x30,
		.name = "ov2659",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = ov2659_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV3640
	{
		.addr = 0x3c,
		.name = "ov3640",
		.pwdn = 1,
		.max_cap_size = SIZE_2048X1536;
		.probe_func = ov3640_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV3660
	{
		.addr = 0x3c,
		.name = "ov3660",
		.pwdn = 1,
		.max_cap_size = SIZE_2048X1536,
		.probe_func = ov3660_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV5640
	{
		.addr = 0x3c,
		.name = "ov5640",
		.pwdn = 1,
		.max_cap_size = SIZE_2592X1944,
		.probe_func = ov5640_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV5642
	{
		.addr = 0x3c,
		.name = "ov5642",
		.pwdn = 1,
		.max_cap_size = SIZE_2592X1944,
		.probe_func = ov5642_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV5647
	{
		.addr = 0x36, /* really value should be 0x6c  */
		.name = "ov5647", .pwdn = 1, .max_cap_size = SIZE_2592X1944,
		.probe_func = ov5647_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_OV7675
	{
		.addr = 0x21,
		.name = "ov7675",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = ov7675_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP0A19
	{
		.addr = 0x21,
		.name = "sp0a19",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = sp0a19_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP0838
	{
		.addr = 0x18,
		.name = "sp0838",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = sp0838_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP2518
	{
		.addr = 0x30,
		.name = "sp2518",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = sp2518_v4l2_probe,
	},
#endif

#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HI253
	{
		.addr = 0x20,
		.name = "hi253",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = hi253_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HM5065
	{
		.addr = 0x1f,
		.name = "hm5065",
		.pwdn = 0,
		.max_cap_size = SIZE_2592X1944,
		.probe_func = hm5065_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HM1375
	{
		.addr = 0x24,
		.name = "hm1375",
		.pwdn = 1,
		.max_cap_size = SIZE_1280X1024,
		.probe_func = hm1375_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_HI2056
	{
		.addr = 0x24,
		.name = "mipi-hi2056",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = hi2056_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_AR0543
	{
		.addr = 0x36,
		.name = "ar0543",
		.pwdn = 0,
		.max_cap_size = SIZE_2592X1944,
		.probe_func = ar0543_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_AR0833
	{
		.addr = 0x36,
		.name = "ar0833",
		.pwdn = 0,
		.max_cap_size = SIZE_2592X1944,
		.probe_func = ar0833_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_SP1628
	{
		.addr = 0x3c,
		.name = "sp1628",
		.pwdn = 1,
		.max_cap_size = SIZE_1280X960,
		.probe_func = sp1628_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_BF3720
	{
		.addr = 0x6e,
		.name = "bf3720",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = bf3720_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_BF3703
	{
		.addr = 0x6e,
		.name = "bf3703",
		.pwdn = 1,
		.max_cap_size = SIZE_640X480,
		.probe_func = bf3703_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_BF3920
	{
		.addr = 0x6e,
		.name = "bf3920",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = bf3920_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2145
	{
		.addr = 0x3c,
		.name = "gc2145",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = gc2145_v4l2_probe,
	},
#endif
#ifdef CONFIG_AMLOGIC_VIDEO_CAPTURE_GC2145_MIPI
	{
		.addr = 0x3c,
		.name = "gc2145_mipi",
		.pwdn = 1,
		.max_cap_size = SIZE_1600X1200,
		.probe_func = gc2145_mipi_v4l2_probe,
	},
#endif
};

static const struct aml_cam_dev_info_s *get_cam_info_by_name(const char *name)
{
	int i;

	if (!name)
		return NULL;
	/*pr_info("cam_devs num is %d\n", ARRAY_SIZE(cam_devs));*/
	for (i = 0; i < ARRAY_SIZE(cam_devs); i++) {
		if (!strcmp(name, cam_devs[i].name)) {
			pr_info("camera dev %s found\n", cam_devs[i].name);
			pr_info("camera i2c addr: 0x%x\n", cam_devs[i].addr);
			return &cam_devs[i];
		}
	}
	return NULL;
}

struct res_item {
	enum resolution_size size;
	char *name;
};

static const struct res_item res_item_array[] = {
	{SIZE_320X240, "320X240"}, {
		SIZE_640X480, "640X480"
	}, {SIZE_720X405, "720X405"}, {
		SIZE_800X600, "800X600"
	}, {SIZE_960X540, "960X540"}, {
		SIZE_1024X576, "1024X576"
	}, {SIZE_960X720, "960X720"}, {
		SIZE_1024X768, "1024X768"
	}, {SIZE_1280X720, "1280X720"}, {
		SIZE_1152X864, "1152X864"
	}, {SIZE_1366X768, "1366X768"}, {
		SIZE_1280X960, "1280X960"
	}, {SIZE_1280X1024, "1280X1024"}, {
		SIZE_1400X1050, "1400X1050"
	}, {SIZE_1600X900, "1600X900"}, {
		SIZE_1600X1200, "1600X1200"
	}, {SIZE_1920X1080, "1920X1080"}, {
		SIZE_1792X1344, "1792X1344"
	}, {SIZE_2048X1152, "2048X1152"}, {
		SIZE_2048X1536, "2048X1536"
	}, {SIZE_2304X1728, "2304X1728"}, {
		SIZE_2560X1440, "2560X1440"
	}, {SIZE_2592X1944, "2592X1944"}, {
		SIZE_3072X1728, "3072X1728"
	}, {SIZE_2816X2112, "2816X2112"}, {
		SIZE_3072X2304, "3072X2304"
	}, {SIZE_3200X2400, "3200X2400"}, {
		SIZE_3264X2448, "3264X2448"
	}, {SIZE_3840X2160, "3840X2160"}, {
		SIZE_3456X2592, "3456X2592"
	}, {SIZE_3600X2700, "3600X2700"}, {
		SIZE_4096X2304, "4096X2304"
	}, {SIZE_3672X2754, "3672X2754"}, {
		SIZE_3840X2880, "3840X2880"
	}, {SIZE_4000X3000, "4000X3000"}, {
		SIZE_4608X2592, "4608X2592"
	}, {SIZE_4096X3072, "4096X3072"}, {
		SIZE_4800X3200, "4800X3200"
	}, {SIZE_5120X2880, "5120X2880"}, {
		SIZE_5120X3840, "5120X3840"
	}, {SIZE_6400X4800, "6400X480"},

};

static enum resolution_size get_res_size(const char *res_str)
{
	enum resolution_size ret = SIZE_NULL;
	const struct res_item *item;
	int i;

	if (!res_str)
		return SIZE_NULL;
	for (i = 0; i < ARRAY_SIZE(res_item_array); i++) {
		item = &res_item_array[i];
		if (!strcmp(item->name, res_str)) {
			ret = item->size;
			return ret;
		}
	}

	return ret;
}

static inline void GXBB_cam_enable_clk(void)
{
	struct clk *clk;

	clk = clk_get(&cam_pdev->dev, "clk_camera_24");
	if (IS_ERR(clk)) {
		pr_info("cannot get camera m-clock\n");
		clk = NULL;
	} else {
		cam_clk = clk;
		clk_prepare_enable(clk);
	}
}

static inline void GXBB_cam_disable_clk(int spread_spectrum)
{
	if (cam_clk) {
		clk_disable_unprepare(cam_clk);
		clk_put(cam_clk);
	}
}

static inline void GX12_cam_enable_clk(void)
{
	struct clk *clk;
	unsigned long clk_rate;

	clk = devm_clk_get(&cam_pdev->dev, "g12a_24m");
	if (IS_ERR(clk)) {
		pr_info("cannot get camera m-clock\n");
		clk = NULL;
	} else {
		cam_clk = clk;
		clk_set_rate(clk, 24000000);
		clk_prepare_enable(clk);
		clk_rate = clk_get_rate(clk);
	}
}

static inline void GX12_cam_disable_clk(int spread_spectrum)
{
	if (cam_clk) {
		clk_disable_unprepare(cam_clk);
		devm_clk_put(&cam_pdev->dev, cam_clk);
		pr_info("Success disable mclk\n");
	}
}

static inline void cam_enable_clk(int clk, int spread_spectrum)
{
	pr_err("camera mclk enable failed, unsupport chip\n");
}

static inline void cam_disable_clk(int spread_spectrum)
{
	pr_err("camera mclk disable failed, unsupport chip\n");
}

/*static struct platform_device *cam_pdev;*/

void aml_cam_init(struct aml_cam_info_s *cam_dev)
{
	/*select XTAL as camera clock*/
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
	if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB)
		GXBB_cam_enable_clk();
	else
#endif
	if ((get_cpu_type() == MESON_CPU_MAJOR_ID_G12A) ||
		(get_cpu_type() == MESON_CPU_MAJOR_ID_G12B) ||
		(is_meson_sm1_cpu()))
		GX12_cam_enable_clk();
	else
		cam_enable_clk(cam_dev->mclk, cam_dev->spread_spectrum);

	/*coding style need: msleep < 20ms can sleep for up to 20ms*/
	msleep(20);
	/*set camera power enable*/
	if (!cam_dev->front_back) {
		cam_dev->pwdn_pin = camera0_pwdn_pin;
		cam_dev->rst_pin = camera0_rst_pin;
	} else {
		cam_dev->pwdn_pin = camera1_pwdn_pin;
		cam_dev->rst_pin = camera1_rst_pin;
	}

	gpio_direction_output(cam_dev->pwdn_pin, cam_dev->pwdn_act);

	msleep(20);

	gpio_direction_output(cam_dev->pwdn_pin, !(cam_dev->pwdn_act));

	msleep(20);

	gpio_direction_output(cam_dev->rst_pin, 0);

	msleep(20);

	gpio_direction_output(cam_dev->rst_pin, 1);

	msleep(20);

	pr_info("aml_cams: %s init OK\n", cam_dev->name);
}

void aml_cam_uninit(struct aml_cam_info_s *cam_dev)
{
	int ret;

	pr_info("aml_cams: %s uninit.\n", cam_dev->name);
	/*set camera power disable*/
	/*coding style need: msleep < 20ms can sleep for up to 20ms*/
	/*msleep(20);*/

	ret = gpio_direction_output(cam_dev->pwdn_pin, cam_dev->pwdn_act);
	if (ret < 0)
		pr_info("%s pwdn_pin output pwdn_act failed\n", __func__);

	msleep(20);

	ret = gpio_direction_output(cam_dev->rst_pin, 0);
	if (ret < 0)
		pr_info("%s rst_pin output rst_pin failed\n", __func__);

	msleep(20);

#ifndef CONFIG_AMLOGIC_REMOVE_OLD
	if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB)
		GXBB_cam_disable_clk(cam_dev->spread_spectrum);
	else
#endif
	if ((get_cpu_type() == MESON_CPU_MAJOR_ID_G12A) ||
		(get_cpu_type() == MESON_CPU_MAJOR_ID_G12B) ||
		(is_meson_sm1_cpu()))
		GX12_cam_disable_clk(cam_dev->spread_spectrum);
	else
		cam_disable_clk(cam_dev->spread_spectrum);
}

void aml_cam_flash(struct aml_cam_info_s *cam_dev, int is_on)
{
	int ret;

	if (cam_dev->flash_support) {
		pr_info("aml_cams: %s flash %s.\n",
		    cam_dev->name, is_on ? "on" : "off");

		ret = gpio_direction_output(cam_dev->flash_ctrl_pin,
			    cam_dev->flash_ctrl_level ? is_on : !is_on);
		if (ret < 0)
			pr_info("%s flash_ctrl_pin output failed\n", __func__);
	}
}

void aml_cam_torch(struct aml_cam_info_s *cam_dev, int is_on)
{
	int ret;

	if (cam_dev->torch_support) {
		pr_info("aml_cams: %s torch %s.\n",
	    cam_dev->name, is_on ? "on" : "off");

		ret = gpio_direction_output(cam_dev->torch_ctrl_pin,
			    cam_dev->torch_ctrl_level ? is_on : !is_on);
		if (ret < 0)
			pr_info("%s torch_ctrl_pin output failed\n", __func__);
	}
}

static struct list_head cam_head = LIST_HEAD_INIT(cam_head);

#define DEBUG_DUMP_CAM_INFO

static int fill_csi_dev(struct device_node *p_node,
			struct aml_cam_info_s *cam_dev)
{
	const char *str;
	int ret = 0;

	ret = of_property_read_string(p_node, "clk_channel", &str);
	if (ret) {
		pr_info("failed to read clock channel, \"a or b\"\n");
		cam_dev->clk_channel = CLK_CHANNEL_A;
	} else {
		pr_info("clock channel:clk %s\n", str);
		if (strncmp("a", str, 1) == 0)
			cam_dev->clk_channel = CLK_CHANNEL_A;
		else
			cam_dev->clk_channel = CLK_CHANNEL_B;
	}

	return ret;
}

static int fill_cam_dev(struct device_node *p_node,
			struct aml_cam_info_s *cam_dev)
{
	const char *str;
	int ret = 0;
	const struct aml_cam_dev_info_s *cam_info = NULL;
	struct device_node *adapter_node = NULL;
	struct i2c_adapter *adapter = NULL;
	unsigned int mclk = 0;
	unsigned int vcm_mode = 0;

	if (!p_node || !cam_dev)
		return -1;

	ret = of_property_read_u32(p_node, "front_back", &cam_dev->front_back);
	if (ret) {
		pr_info("get camera name failed!\n");
		goto err_out;
	}

	ret = of_property_read_string(p_node, "cam_name", &cam_dev->name);
	if (ret) {
		pr_info("get camera name failed!\n");
		goto err_out;
	}

	cam_dev->cam_vdd = of_get_named_gpio(p_node, "camvdd-gpios", 0);
	pr_info("cam_dev->cam_vdd = %d\n", cam_dev->cam_vdd);
	if (cam_dev->cam_vdd > 0) {
		ret = gpio_request(cam_dev->cam_vdd, "camera");
		if (ret < 0)
			pr_info("aml_cam_init cam_vdd request failed\n");
		else
			gpio_direction_output(cam_dev->cam_vdd, 0);
	} else {
		pr_info("%s: failed to map gpio_cam_vdd !\n", cam_dev->name);
	}
	cam_dev->pwdn_pin = of_get_named_gpio(p_node, "gpio_pwdn-gpios", 0);
	if (cam_dev->pwdn_pin == 0) {
		pr_info("%s: failed to map gpio_pwdn !\n", cam_dev->name);
		goto err_out;
	}

	ret = gpio_request(cam_dev->pwdn_pin, "camera");
	if (ret < 0)
		pr_info("aml_cam_init pwdn_pin request failed\n");

	if (!cam_dev->front_back)
		camera0_pwdn_pin = cam_dev->pwdn_pin;
	else
		camera1_pwdn_pin = cam_dev->pwdn_pin;

	cam_dev->rst_pin = of_get_named_gpio(p_node, "gpio_rst-gpios", 0);
	if (cam_dev->rst_pin == 0) {
		pr_info("%s: failed to map gpio_rst !\n", cam_dev->name);
		goto err_out;
	}
	ret = gpio_request(cam_dev->rst_pin, "camera");
	if (ret < 0)
		pr_info("aml_cam_init rst_pin request failed\n");

	if (!cam_dev->front_back)
		camera0_rst_pin = cam_dev->rst_pin;
	else
		camera1_rst_pin = cam_dev->rst_pin;

	cam_info = get_cam_info_by_name(cam_dev->name);
	if (!cam_info) {
		pr_info("camera %s is not support\n", cam_dev->name);
		ret = -1;
		goto err_out;
	}

	of_property_read_u32(p_node, "spread_spectrum",
		&cam_dev->spread_spectrum);

	ret = of_property_read_string(p_node, "bt_path", &str);
	if (ret) {
		pr_info("failed to read bt_path\n");
		cam_dev->bt_path = BT_PATH_GPIO;
	} else {
		pr_info("bt_path :%d\n", cam_dev->bt_path);
		if (strncmp("csi", str, 3) == 0)
			cam_dev->bt_path = BT_PATH_CSI2;
		else if (strncmp("gpio_b", str, 6) == 0)
			cam_dev->bt_path = BT_PATH_GPIO_B;
		else
			cam_dev->bt_path = BT_PATH_GPIO;
	}
	of_property_read_u32(p_node, "bt_path_count", &cam_dev->bt_path_count);
	bt_path_count = cam_dev->bt_path_count;

	cam_dev->pwdn_act = cam_info->pwdn;
	cam_dev->i2c_addr = cam_info->addr;
	pr_info("camera addr: 0x%x\n", cam_dev->i2c_addr);
	pr_info("camera i2c bus: %d\n", cam_dev->i2c_bus_num);

	adapter_node = of_parse_phandle(p_node, "camera-i2c-bus", 0);
	if (adapter_node) {
		adapter = of_find_i2c_adapter_by_node(adapter_node);
		of_node_put(adapter_node);
		if (!adapter) {
			pr_err("%s, failed parse camera-i2c-bus\n",
			   __func__);
			return -1;
		}
	}

	if (adapter && cam_info->probe_func) {
		aml_cam_init(cam_dev);
		if (cam_info->probe_func(adapter) != 1) {
			pr_info("camera %s not on board\n", cam_dev->name);
			ret = -1;
			aml_cam_uninit(cam_dev);
			goto err_out;
		}
		aml_cam_uninit(cam_dev);
	} else {
		pr_err("can not do probe function\n");
		ret = -1;
		goto err_out;
	}

	of_property_read_u32(p_node, "mirror_flip", &cam_dev->m_flip);
	of_property_read_u32(p_node, "vertical_flip", &cam_dev->v_flip);
	of_property_read_u32(p_node, "vdin_path", &cam_dev->vdin_path);

	ret = of_property_read_string(p_node, "max_cap_size", &str);
	if (ret) {
		pr_info("failed to read max_cap_size\n");
	} else {
		pr_info("max_cap_size :%s\n", str);
		cam_dev->max_cap_size = get_res_size(str);
	}
	if (cam_dev->max_cap_size == SIZE_NULL)
		cam_dev->max_cap_size = cam_info->max_cap_size;

	ret = of_property_read_u32(p_node, "mclk", &mclk);
	if (ret)
		cam_dev->mclk = 24000;
	else
		cam_dev->mclk = mclk;

	ret = of_property_read_u32(p_node, "vcm_mode", &vcm_mode);
	if (ret)
		cam_dev->vcm_mode = 0;
	else
		cam_dev->vcm_mode = vcm_mode;
	pr_info("vcm mode is %d\n", cam_dev->vcm_mode);

	ret = of_property_read_u32(p_node, "flash_support",
				   &cam_dev->flash_support);
	if (cam_dev->flash_support) {
		of_property_read_u32(p_node, "flash_ctrl_level",
				     &cam_dev->flash_ctrl_level);
		ret = of_property_read_string(p_node, "flash_ctrl_pin", &str);
		if (ret) {
			pr_info("%s: failed to get flash_ctrl_pin!\n",
			cam_dev->name);
			cam_dev->flash_support = 0;
		} else {
			cam_dev->flash_ctrl_pin = of_get_named_gpio(p_node,
			    "flash_ctrl_pin", 0);
			if (cam_dev->flash_ctrl_pin == 0) {
				pr_info("%s: failed to map flash_ctrl_pin !\n",
			    cam_dev->name);
				cam_dev->flash_support = 0;
				cam_dev->flash_ctrl_level = 0;
			}
			ret = gpio_request(cam_dev->flash_ctrl_pin, "camera");
			if (ret < 0)
				pr_info("camera flash_ctrl_pin request failed\n");
		}
	}

	ret = of_property_read_u32(p_node, "torch_support",
				   &cam_dev->torch_support);
	if (cam_dev->torch_support) {
		of_property_read_u32(p_node, "torch_ctrl_level",
				     &cam_dev->torch_ctrl_level);
		ret = of_property_read_string(p_node, "torch_ctrl_pin", &str);
		if (ret) {
			pr_info("%s: failed to get torch_ctrl_pin!\n",
			    cam_dev->name);
			cam_dev->torch_support = 0;
		} else {
			cam_dev->torch_ctrl_pin = of_get_named_gpio(p_node,
			   "torch_ctrl_level", 0);
			ret = gpio_request(cam_dev->torch_ctrl_pin, "camera");
			if (ret < 0)
				pr_info("camera torch_ctrl_pin request failed\n");
		}
	}

	ret = of_property_read_string(p_node, "interface", &str);
	if (ret) {
		pr_info("failed to read camera interface \"mipi or dvp\"\n");
		cam_dev->interface = CAM_DVP;
	} else {
		pr_info("camera interface:%s\n", str);
		if (strncmp("dvp", str, 1) == 0)
			cam_dev->interface = CAM_DVP;
		else
			cam_dev->interface = CAM_MIPI;
	}
	if (cam_dev->interface == CAM_MIPI) {
		ret = fill_csi_dev(p_node, cam_dev);
		if (ret < 0)
			goto err_out;
	}

	ret = of_property_read_string(p_node, "bayer_fmt", &str);
	if (ret) {
		pr_info("failed to read camera bayer fmt\n");
		cam_dev->bayer_fmt = TVIN_GBRG;
	} else {
		pr_info("color format:%s\n", str);
		if (strncmp("BGGR", str, 4) == 0)
			cam_dev->bayer_fmt = TVIN_BGGR;
		else if (strncmp("RGGB", str, 4) == 0)
			cam_dev->bayer_fmt = TVIN_RGGB;
		else if (strncmp("GBRG", str, 4) == 0)
			cam_dev->bayer_fmt = TVIN_GBRG;
		else if (strncmp("GRBG", str, 4) == 0)
			cam_dev->bayer_fmt = TVIN_GRBG;
		else
			cam_dev->bayer_fmt = TVIN_GBRG;
	}

	ret = of_property_read_string(p_node, "config_path", &cam_dev->config);
	if (ret)
		pr_info("failed to read config_file path\n");
	else
		pr_info("config path :%s\n", cam_dev->config);

#ifdef DEBUG_DUMP_CAM_INFO
	pr_info("=======cam %s info=======\n"
	       "i2c_bus_num: %d\n"
	       "pwdn_act: %d\n"
	       "front_back: %d\n"
	       "m_flip: %d\n"
	       "v_flip: %d\n"
	       "i2c_addr: 0x%x\n"
	       "config path:%s\n"
	       "bt_path:%d\n",
	       cam_dev->name, cam_dev->i2c_bus_num, cam_dev->pwdn_act,
	       cam_dev->front_back, cam_dev->m_flip, cam_dev->v_flip,
	       cam_dev->i2c_addr, cam_dev->config, cam_dev->bt_path);
#endif /* DEBUG_DUMP_CAM_INFO */

	ret = 0;

err_out:
	return ret;
}

static int do_read_work(char argn, char **argv)
{
	unsigned int dev_addr, reg_addr, data_len = 1, result;
	unsigned int i2c_bus;
	struct i2c_adapter *adapter;

	if (argn < 4) {
		pr_err("args num error");
		return -1;
	}

	if (!strncmp(argv[1], "i2c_bus_ao", 9)) {
		i2c_bus = 4;
	} else if (!strncmp(argv[1], "i2c_bus_0", 9)) {
		i2c_bus = 0;
	} else if (!strncmp(argv[1], "i2c_bus_1", 9)) {
		i2c_bus = 1;
	} else if (!strncmp(argv[1], "i2c_bus_2", 9)) {
		i2c_bus = 2;
	} else if (!strncmp(argv[1], "i2c_bus_3", 9)) {
		i2c_bus = 3;
	} else {
		pr_err("bus name error!\n");
		return -1;
	}

	adapter = i2c_get_adapter(i2c_bus);

	if (!adapter) {
		pr_info("no adapter!\n");
		return -1;
	}

	dev_addr = kstrtol(argv[2], 16, NULL);
	reg_addr = kstrtol(argv[3], 16, NULL);
	if (argn == 5) {
		pr_info("argv[4] is %s\n", argv[4]);
		data_len = kstrtol(argv[4], 16, NULL);
	}

	if (reg_addr > 256) {
		if (data_len != 2) {
			result = aml_i2c_get_byte(adapter, dev_addr, reg_addr);
			pr_info("register [0x%04x]=0x%02x\n", reg_addr, result);
		} else {
			result = aml_i2c_get_word(adapter, dev_addr, reg_addr);
			pr_info("register [0x%04x]=0x%04x\n", reg_addr, result);
		}
	} else {
		if (data_len != 2) {
			result = aml_i2c_get_byte_add8(adapter,
			dev_addr, reg_addr);
			pr_info("register [0x%02x]=0x%02x\n", reg_addr, result);
		} else {
			result = aml_i2c_get_word_add8(adapter,
			dev_addr, reg_addr);
			pr_info("register [0x%02x]=0x%04x\n", reg_addr, result);
		}
	}

	return 0;
}

static int do_write_work(char argn, char **argv)
{
	unsigned int dev_addr, reg_addr, reg_val, data_len = 1, ret = 0;
	unsigned int i2c_bus;
	struct i2c_adapter *adapter;

	if (argn < 5) {
		pr_err("args num error");
		return -1;
	}

	if (!strncmp(argv[1], "i2c_bus_0", 9)) {
		i2c_bus = 0;
	} else if (!strncmp(argv[1], "i2c_bus_1", 9)) {
		i2c_bus = 1;
	} else if (!strncmp(argv[1], "i2c_bus_2", 9)) {
		i2c_bus = 2;
	} else if (!strncmp(argv[1], "i2c_bus_3", 9)) {
		i2c_bus = 3;
	} else if (!strncmp(argv[1], "i2c_bus_ao", 9)) {
		i2c_bus = 4;
	} else {
		pr_err("bus name error!\n");
		return -1;
	}

	adapter = i2c_get_adapter(i2c_bus);

	if (!adapter) {
		pr_info(" no adapter!\n");
		return -1;
	}

	dev_addr = kstrtol(argv[2], 16, NULL);
	reg_addr = kstrtol(argv[3], 16, NULL);
	reg_val = kstrtol(argv[4], 16, NULL);
	if (argn == 6)
		data_len = kstrtol(argv[5], 16, NULL);
	if (reg_addr > 256) {
		if (data_len != 2) {
			if (aml_i2c_put_byte(adapter, dev_addr,
			    reg_addr, reg_val) < 0) {
				pr_err("write error\n");
				ret = -1;
			} else {
				pr_info("write ok\n");
				ret = 0;
			}
		} else {
			if (aml_i2c_put_word(adapter, dev_addr,
			    reg_addr, reg_val) < 0) {
				pr_err("write error\n");
				ret = -1;
			} else {
				pr_info("write ok\n");
				ret = 0;
			}
		}
	} else {
		if (data_len != 2) {
			if (aml_i2c_put_byte_add8(adapter, dev_addr,
			    reg_addr, reg_val) < 0) {
				pr_err("write error\n");
				ret = -1;
			} else {
				pr_info("write ok\n");
				ret = 0;
			}
		} else {
			if (aml_i2c_put_word_add8(adapter, dev_addr,
			    reg_addr, reg_val) < 0) {
				pr_err("write error\n");
				ret = -1;
			} else {
				pr_info("write ok\n");
				ret = 0;
			}
		}
	}

	return ret;
}

static struct class *cam_clsp;

static ssize_t show_help(struct class *class, struct class_attribute *attr,
			 char *buf)
{
	ssize_t size = 0;

	pr_info("echo [read | write] i2c_bus_type device_address register_address [value] [data_len] > i2c_debug\n"
		"i2c_bus_type are: i2c_bus_ao, i2c_bus_a, i2c_bus_b, i2c_bus_c, i2c_bus_d\n"
		"e.g.: echo read i2c_bus_ao 0x3c 0x18 1\n"
		"      echo write i2c_bus_ao 0x3c 0x18 0x24 1\n");
	return size;
}

static ssize_t store_i2c_debug(struct class *class,
			       struct class_attribute *attr, const char *buf,
			       size_t count)
{
	int argn;
	char *buf_work, *p, *para;
	char cmd;
	char *argv[6];

	buf_work = kstrdup(buf, GFP_KERNEL);
	p = buf_work;

	for (argn = 0; argn < 6; argn++) {
		para = strsep(&p, " ");
		if (!para)
			break;
		argv[argn] = para;
		pr_info("argv[%d] = %s\n", argn, para);
	}

	if (argn < 4 || argn > 6)
		goto end;

	cmd = argv[0][0];
	switch (cmd) {
	case 'r':
	case 'R':
		do_read_work(argn, argv);
		break;
	case 'w':
	case 'W':
		do_write_work(argn, argv);
		break;
	}
	return count;
end:
	pr_err("error command!\n");
	kfree(buf_work);
	return -EINVAL;
}

static LIST_HEAD(info_head);

static ssize_t cam_info_show(struct class *class, struct class_attribute *attr,
			     char *buf)
{
	struct list_head *p;
	struct aml_cam_info_s *cam_info = NULL;
	int count = 0;

	if (!list_empty(&info_head)) {
		count += sprintf(&buf[count],
	    "name\t\tversion\t\t\t\tface_dir\t"
			 "i2c_addr\n");
		list_for_each(p, &info_head) {
			cam_info = list_entry(p, struct aml_cam_info_s,
			    info_entry);
		}
	}
	return count;
}

static struct class_attribute aml_cam_attrs[] = {
	__ATTR(i2c_debug, 0644, show_help, store_i2c_debug),
	__ATTR_RO(cam_info), __ATTR(help, 0644, show_help, NULL),
	__ATTR_NULL,
};

int aml_cam_info_reg(struct aml_cam_info_s *cam_info)
{
	int ret = -1;

	if (cam_info) {
		list_add(&cam_info->info_entry, &info_head);
		ret = 0;
	}
	return ret;
}

int aml_cam_info_unreg(struct aml_cam_info_s *cam_info)
{
	int ret = -1;
	struct list_head *p, *n;
	struct aml_cam_info_s *tmp_info = NULL;

	if (cam_info) {
		list_for_each_safe(p, n, &info_head) {
			tmp_info = list_entry(p, struct aml_cam_info_s,
				    info_entry);
			if (tmp_info == cam_info) {
				list_del(p);
				return 0;
			}
		}
	}
	return ret;
}

static int aml_cams_probe(struct platform_device *pdev)
{
	struct device_node *cams_node = pdev->dev.of_node;
	struct device_node *child;
	//struct i2c_board_info board_info;
	struct i2c_adapter *adapter = NULL;
	struct device_node *adapter_node = NULL;
	struct timeval camera_start;
	struct timeval camera_end;
	int i;
	unsigned long time_use = 0;

	temp_cam = kzalloc(sizeof(*temp_cam), GFP_KERNEL);
	if (!temp_cam)
		return -ENOMEM;

	cam_pdev = pdev;
	do_gettimeofday(&camera_start);
	for_each_child_of_node(cams_node, child) {
		memset(temp_cam, 0, sizeof(*temp_cam));

		if (fill_cam_dev(child, temp_cam))
			continue;

		adapter_node = of_parse_phandle(child, "camera-i2c-bus", 0);
		if (adapter_node) {
			adapter = of_get_i2c_adapter_by_node(adapter_node);
			of_node_put(adapter_node);
			if (!adapter) {
				pr_err("%s, failed parse camera-i2c-bus\n",
				   __func__);
				return -1;
			}
		} else {
			pr_err("adapter node is NULL.\n");
			return -1;
		}
		pr_info("new i2c device\n");
	}
	do_gettimeofday(&camera_end);
	time_use = (camera_end.tv_sec - camera_start.tv_sec) * 1000 +
		(camera_end.tv_usec - camera_start.tv_usec) / 1000;
	pr_info("camera probe cost time = %ldms\n", time_use);

	pr_info("aml probe finish\n");
	cam_clsp = class_create(THIS_MODULE, "aml_camera");
	for (i = 0; aml_cam_attrs[i].attr.name; i++) {
		if (class_create_file(cam_clsp, &aml_cam_attrs[i]) < 0)
			return -1;
	}

	return 0;
}

static int aml_cams_remove(struct platform_device *pdev)
{
	if (camera0_pwdn_pin != 0)
		gpio_free(camera0_pwdn_pin);
	if (camera0_rst_pin != 0)
		gpio_free(camera0_rst_pin);
	if (camera1_pwdn_pin != 0)
		gpio_free(camera1_pwdn_pin);
	if (camera1_rst_pin != 0)
		gpio_free(camera1_rst_pin);

	kfree(temp_cam);
	temp_cam = NULL;

	return 0;
}

static const struct of_device_id cams_prober_dt_match[] = {{
		.compatible =
		"amlogic, cams_prober",
	}, {},
};

static struct platform_driver aml_cams_prober_driver = {
	.probe = aml_cams_probe, .remove = aml_cams_remove, .driver = {
		.name =
		"aml_cams_prober", .owner = THIS_MODULE, .of_match_table =
		cams_prober_dt_match,
	},
};

int __init aml_cams_prober_init(void)
{
	if (platform_driver_register(&aml_cams_prober_driver)) {
		pr_err("aml_cams_probre_driver register failed\n");
		return -ENODEV;
	}
	flashlight_init();
	vm_init_module();

	return 0;
}

static void __exit aml_cams_prober_exit(void)
{
	bt_path_count = 0;
	platform_driver_unregister(&aml_cams_prober_driver);
	flashlight_exit();
	vm_remove_module();
}

module_init(aml_cams_prober_init);
module_exit(aml_cams_prober_exit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Amlogic Cameras prober driver");

