// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_irq.h>

/* #include <mach/am_regs.h> */
/* #include <mach/mod_gate.h> */

#include "../tvin_global.h"
#include "../tvin_format_table.h"
#include "../tvin_frontend.h"
#include "bt656_601_in.h"

#define BT656_DEV_NAME  "amvdec_656in"
#define BT656_DRV_NAME  "amvdec_656in"
#define BT656_CLS_NAME  "amvdec_656in"
#define BT656_MOD_NAME  "amvdec_656in"

/* #define HANDLE_BT656IN_IRQ */

#define BT656_MAX_DEVS             2
/* #define BT656IN_ANCI_DATA_SIZE        0x4000 */

#define BT656_VER "2017/08/23"

/* Per-device (per-bank) structure */

/* static struct struct am656in_dev_s am656in_dev_; */
static dev_t am656in_devno;
static struct class *am656in_clsp;
static unsigned char hw_cnt;
static struct meson_bt656in_data bt656_data;
static struct meson_bt656in_data *am_bt656_data;
static struct am656in_dev_s *am656in_devp[BT656_MAX_DEVS];

#ifdef HANDLE_BT656IN_IRQ
static const char bt656in_dec_id[] = "bt656in-dev";
#endif

static struct mutex bt656_mutex;

/* bt656 debug function */
static ssize_t reg_store(struct device *dev,
			 struct device_attribute *attr,
			 const char *buff, size_t count)
{
	unsigned int argn = 0, addr = 0, value = 0, end = 0;
	char *p, *para, *buf_work, cmd = 0;
	char *argv[3];
	long val;
	struct am656in_dev_s *devp = dev_get_drvdata(dev);

	if ((devp->dec_status & TVIN_AM656_RUNNING) == 0) {
		BT656ERR("bt656[%d] is not running.\n", devp->index);
		return count;
	}

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

	for (argn = 0; argn < 3; argn++) {
		para = strsep(&p, " ");
		if (!para)
			break;
		argv[argn] = para;
	}

	if (argn < 1 || argn > 3)
		goto reg_store_exit;

	cmd = argv[0][0];
	switch (cmd) {
	case 'r':
	case 'R':
		if (argn < 2) {
			pr_err("syntax error.\n");
		} else {
			/* addr = simple_strtol(argv[1], NULL, 16); */
			if (kstrtol(argv[1], 16, &val) < 0)
				break;
			addr = val;
			value = bt656_rd(devp->index, addr);
			pr_info("reg[%d:0x%2x]=0x%08x\n",
				devp->index, addr, value);
		}
		break;
	case 'w':
	case 'W':
		if (argn < 3) {
			pr_err("syntax error.\n");
		} else {
			/* value = simple_strtol(argv[1], NULL, 16); */
			if (kstrtol(argv[1], 16, &val) < 0)
				break;
			value = val;
			/* addr = simple_strtol(argv[2], NULL, 16); */
			if (kstrtol(argv[2], 16, &val) < 0)
				break;
			addr = val;
			bt656_wr(devp->index, addr, value);
			pr_info("Write reg[%d:0x%2x]=0x%08x\n",
				devp->index, addr,
				bt656_rd(devp->index, addr));
		}
		break;
	case 'd':
	case 'D':
		/* if (argn < 3) { */
		/* pr_err("syntax error.\n"); */
		/* } else { */
			/* addr = simple_strtol(argv[1], NULL, 16); */
			/* if (kstrtol(argv[1], 16, &val) < 0) */
			/* break; */
			/* addr = val; */
			/* end = simple_strtol(argv[2], NULL, 16); */
			/* if (kstrtol(argv[2], 16, &val) < 0) */
			/* break; */
			/* end = val; */
			addr = 0;
			end = 0x1f;
			for (; addr <= end; addr++)
				pr_info("reg[%d:0x%2x]=0x%08x\n",
					devp->index, addr,
					bt656_rd(devp->index, addr));
		/* } */
		break;
	default:
		pr_err("not support.\n");
		break;
	}

reg_store_exit:
	kfree(buf_work);
	return count;
}

static ssize_t reg_show(struct device *dev,
			struct device_attribute *attr, char *buff)
{
	ssize_t len = 0;

	len += sprintf(buff + len, "Usage:\n");
	len += sprintf(buff + len,
		"\techo [read|write <data>] addr > reg;\n");
	len += sprintf(buff + len,
		"\techo dump > reg; Dump regs.\n");
	len += sprintf(buff + len, "Address format:\n");
	len += sprintf(buff + len,
		       "\taddr : 0xXXXX, 8 bits register address\n");

	return len;
}
static DEVICE_ATTR_RW(reg);

static void init_656in_dec_parameter(struct am656in_dev_s *devp)
{
	enum tvin_sig_fmt_e fmt;
	const struct tvin_format_s *fmt_info_p;

	fmt = devp->para.fmt;
	fmt_info_p = tvin_get_fmt_info(fmt);

	if (!fmt_info_p) {
		BT656ERR("bt656[%d] %s:invalid fmt %d.\n",
			 devp->index, __func__, fmt);
		return;
	}

	if (fmt < TVIN_SIG_FMT_MAX) {
		devp->para.v_active    = fmt_info_p->v_active;
		devp->para.h_active    = fmt_info_p->h_active;
		devp->para.hsync_phase = 0;
		devp->para.vsync_phase = 0;
		devp->para.hs_bp       = 0;
		devp->para.vs_bp       = 0;
	}
}

static void init_656in_dec_hdmi_parameter(struct am656in_dev_s *devp)
{
	enum tvin_sig_fmt_e fmt;
	const struct tvin_format_s *fmt_info_p;

	fmt = devp->para.fmt;
	fmt_info_p = tvin_get_fmt_info(fmt);

	if (!fmt_info_p) {
		BT656ERR("bt656[%d] %s:invalid fmt %d.\n",
			 devp->index, __func__, fmt);
		return;
	}

	if (fmt < TVIN_SIG_FMT_MAX) {
		devp->para.v_active    = fmt_info_p->v_active;
		devp->para.h_active    = fmt_info_p->h_active;
		/*devp->para.hsync_phase = fmt_info_p->v_active;
		 *devp->para.vsync_phase = fmt_info_p->v_active;
		 */
		devp->para.hs_bp       = fmt_info_p->hs_bp;
		devp->para.vs_bp       = fmt_info_p->vs_bp;
	}
}

static void reset_bt656in_module(struct am656in_dev_s *devp)
{
	int temp_data = 0;
	unsigned int offset = devp->index;

	temp_data = bt656_rd(offset, BT_CTRL);
	temp_data &= ~(1 << BT_EN_BIT);
	bt656_wr(offset, BT_CTRL, temp_data); /* disable BT656 input */

	/* reset BT656in module. */
	temp_data = bt656_rd(offset, BT_CTRL);
	temp_data |= (1 << BT_SOFT_RESET);
	bt656_wr(offset, BT_CTRL, temp_data);

	temp_data = bt656_rd(offset, BT_CTRL);
	temp_data &= ~(1 << BT_SOFT_RESET);
	bt656_wr(offset, BT_CTRL, temp_data);
}

static int bt656_cpu_type(void)
{
	if (!am_bt656_data) {
		BT656ERR("[%s:%d] am_bt656_data is null", __func__, __LINE__);
		return -1;
	}
	return am_bt656_data->cpu_id;
}

/*
 * NTSC or PAL input(interlace mode): CLOCK + D0~D7(with SAV + EAV )
 */
static void reinit_bt656in_dec(struct am656in_dev_s *devp)
{
	unsigned int offset = devp->index;

	reset_bt656in_module(devp);

	 /* field 0/1 start lcnt: default value */
	bt656_wr(offset, BT_FIELDSADR, (4 << 16) | 4);
	/* configuration the BT PORT control */
	/* For standaREAD_CBUS_REG bt656 in stream,
	 * there's no HSYNC VSYNC pins.
	 */
	/* So we don't need to configure the port. */
	/* data itself is 8 bits. */
	bt656_wr(offset, BT_PORT_CTRL, 1 << BT_D8B);

	bt656_wr(offset, BT_SWAP_CTRL, (4 << 0) |  /* POS_Y1_IN */
			(5 << 4) |        /* POS_Cr0_IN */
			(6 << 8) |        /* POS_Y0_IN */
			(7 << 12));       /* POS_CB0_IN */

	bt656_wr(offset, BT_LINECTRL, 0);
	/* there is no use anci in m2 */
	/* ANCI is the field blanking data, like close caption.
	 * If it connected to digital camera interface,
	 * the jpeg bitstream also use this ANCI FIFO.
	 */
	/* bt656_wr(BT_ANCISADR, devp->mem_start); */
	/* bt656_wr(BT_ANCIEADR, 0); //devp->mem_start + devp->mem_size); */

	bt656_wr(offset, BT_AFIFO_CTRL,   (1 << 31) |
			(1 << 6) |     /* fill _en; */
			(1 << 3));     /* urgent */

	bt656_wr(offset, BT_INT_CTRL,   /* (1 << 5) |    //ancififo done int. */
			/* (1 << 4) |    //SOF interrupt enable. */
			/* (1 << 3) |      //EOF interrupt enable. */
			(1 << 1)); /* | //input overflow interrupt enable. */
	/* (1 << 0)); //bt656 controller error interrupt enable. */

	bt656_wr(offset, BT_ERR_CNT, (626 << 16) | (1760));

	if (devp->para.fmt == TVIN_SIG_FMT_BT656IN_576I_50HZ) {
		/* field 0/1 VBI last line number */
		bt656_wr(offset, BT_VBIEND,   22 | (22 << 16));
		 /* Line number of the first video start line in field 0/1. */
		bt656_wr(offset, BT_VIDEOSTART,   23 | (23 << 16));
		/* Line number of the last video line in field 1.
		 * added video end for avoid overflow.
		 */
		bt656_wr(offset, BT_VIDEOEND,    312 |
				(312 << 16));

		/* Update bt656 status register when end of frame. */
		bt656_wr(offset, BT_CTRL,    (0 << BT_UPDATE_ST_SEL) |
				/* Repeated the color data when
				 * do 4:2:2 -> 4:4:4 data transfer.
				 */
				(1 << BT_COLOR_REPEAT) |
				/* use haREAD_CBUS_REGware to check
				 * the PAL/NTSC format
				 * input format if it's
				 * standaREAD_CBUS_REG BT656 input format.
				 */
				(1 << BT_AUTO_FMT) |
				/* BT656 standaREAD_CBUS_REG interface. */
				(1 << BT_MODE_BIT) |
				(1 << BT_EN_BIT) | /* enable BT module. */
				/* timing reference is from bit stream. */
				(1 << BT_REF_MODE_BIT) |
				/* use external xclk27. */
				(1 << BT_CLK27_SEL_BIT) |
				(1 << BT_XCLK27_EN_BIT)); /* xclk27 is input. */
		/* wr(VDIN_WR_V_START_END, 287 |   */
		/* (0 << 16));   */
	} else {
		/* if(am656in_dec_info.para.fmt  == TVIN_SIG_FMT_BT656IN_480I)
		 * //input is NTSC
		 */

		/* field 0/1 VBI last line number */
		bt656_wr(offset, BT_VBIEND,   21 | (21 << 16));
		/* Line number of the first video start line in field 0/1. */
		bt656_wr(offset, BT_VIDEOSTART,   18 | (18 << 16));
		/* Line number of the last video line in field 1.
		 * added video end for avoid overflow.
		 */
		bt656_wr(offset, BT_VIDEOEND,    257 |
				(257 << 16));

		/* Update bt656 status register when end of frame. */
		bt656_wr(offset, BT_CTRL,    (0 << BT_UPDATE_ST_SEL) |
				/* Repeated the color data when
				 * do 4:2:2 -> 4:4:4 data transfer.
				 */
			(1 << BT_COLOR_REPEAT) |
			/* use haREAD_CBUS_REGware to check the
			 * PAL/NTSC format input format
			 * if it's standaREAD_CBUS_REG
			 *  BT656 input format.
			 */
			(1 << BT_AUTO_FMT) |
				/* BT656 standaREAD_CBUS_REG interface. */
			(1 << BT_MODE_BIT) |
			(1 << BT_EN_BIT) |    /* enable BT module. */
				/* timing reference is from bit stream. */
			(1 << BT_REF_MODE_BIT) |
			(1 << BT_CLK27_SEL_BIT) |    /* use external xclk27. */
			(1 << BT_XCLK27_EN_BIT) |       /* xclk27 is input. */
			(1 << BT_FMT_MODE_BIT));   /* input format is NTSC */
		/* wr(VDIN_WR_V_START_END, 239 | */
		/* (0 << 16));     */
	}
}

/* NTSC or PAL input(interlace mode): CLOCK + D0~D7 + HSYNC + VSYNC + FID */
static void reinit_bt601in_dec(struct am656in_dev_s *devp)
{
	unsigned int offset = devp->index;

	reset_bt656in_module(devp);

	 /* use external idq pin. */
	bt656_wr(offset, BT_PORT_CTRL,    (0 << BT_IDQ_EN)   |
			(1 << BT_IDQ_PHASE)   |
			/* FID came from HS VS. */
			(1 << BT_FID_HSVS) |
			(1 << BT_HSYNC_PHASE) |
			(1 << BT_D8B)     |
			(4 << BT_FID_DELAY) |
			(5 << BT_VSYNC_DELAY) |
			(5 << BT_HSYNC_DELAY));

	/* FID field check done point. */
	bt656_wr(offset, BT_601_CTRL2, (10 << 16));

	/* suppose the input bitstream format is Cb0 Y0 Cr0 Y1. */
	bt656_wr(offset, BT_SWAP_CTRL,    (4 << 0) |
			(5 << 4) |
			(6 << 8) |
			(7 << 13));

	bt656_wr(offset, BT_LINECTRL, (1 << 31) | /*software line ctrl enable*/
			(1644 << 16) |    /* 1440 + 204 */
			220);

	/* ANCI is the field blanking data, like close caption.
	 *  If it connected to digital camera interface,
	 *   the jpeg bitstream also use this ANCI FIFO.
	 */
	/* wr(BT_ANCISADR, devp->mem_start); */
	/* wr(BT_ANCIEADR, 0);//devp->mem_start + devp->mem_size); */

	bt656_wr(offset, BT_AFIFO_CTRL,   (1 << 31) |
			(1 << 6) |     /* fill _en; */
			(1 << 3));     /* urgent */

	bt656_wr(offset, BT_INT_CTRL,   /* (1 << 5) |    //ancififo done int. */
			/* (1 << 4) |  //SOF interrupt enable. */
			/* (1 << 3) | //EOF interrupt enable. */
			(1 << 1)); /* | //input overflow interrupt enable. */
	/* (1 << 0)); //bt656 controller error interrupt enable. */
	bt656_wr(offset, BT_ERR_CNT, (626 << 16) | (2000));
	/* otherwise there is always error flag, */
	/* because the camera input use HREF ont HSYNC, */
	/* there are some lines without HREF sometime */
	bt656_wr(offset, BT_FIELDSADR, (1 << 16) | 1);/* field 0/1 start lcnt */

	if (devp->para.fmt == TVIN_SIG_FMT_BT601IN_576I_50HZ) {
		/* input is PAL */
		/* field 0/1 VBI last line number */
		bt656_wr(offset, BT_VBIEND, 22 | (22 << 16));
		/* Line number of the first video start line in field 0/1. */
		bt656_wr(offset, BT_VIDEOSTART, 23 | (23 << 16));
		/* Line number of the last video line in field 1.
		 * added video end for avoid overflow.
		 */
		bt656_wr(offset, BT_VIDEOEND, 312 |
				(312 << 16));
		/* BT656 standaREAD_CBUS_REG interface. */
		bt656_wr(offset, BT_CTRL,    (0 << BT_MODE_BIT) |
				(1 << BT_AUTO_FMT)     |
				 /* enable BT module. */
				(1 << BT_EN_BIT) |
				/* timing reference is from bit stream. */
				(0 << BT_REF_MODE_BIT) |
				(0 << BT_FMT_MODE_BIT) |     /* PAL */
				(1 << BT_SLICE_MODE_BIT) |
				/* use external fid pin. */
				(0 << BT_FID_EN_BIT)   |
				/* use external xclk27. */
				(1 << BT_CLK27_SEL_BIT) |
				 /* xclk27 is input. */
				(1 << BT_XCLK27_EN_BIT));
		/* wr(VDIN_WR_V_START_END, 287 |  */
		/* (0 << 16));    */
	} else {
	/* if(am656in_dec_info.para.fmt == TVIN_SIG_FMT_BT601IN_480I)
	 * input is NTSC
	 */
		 /* field 0/1 VBI last line number */
		bt656_wr(offset, BT_VBIEND, 21 | (21 << 16));
		/* Line number of the first video start line in field 0/1. */
		bt656_wr(offset, BT_VIDEOSTART, 18 | (18 << 16));
		/* Line number of the last video line in field 1.
		 * added video end for avoid overflow.
		 */
		bt656_wr(offset, BT_VIDEOEND, 257 |
				(257 << 16));
		bt656_wr(offset, BT_CTRL, (0 << BT_MODE_BIT) |
				(1 << BT_AUTO_FMT)     |
				 /* enable656in_star BT module. */
				(1 << BT_EN_BIT) |
				/* timing reference is from bit stream. */
				(0 << BT_REF_MODE_BIT) |
				(1 << BT_FMT_MODE_BIT) |     /* NTSC */
				(1 << BT_SLICE_MODE_BIT) |
				/* use external fid pin. */
				(0 << BT_FID_EN_BIT)   |
				 /* use external xclk27. */
				(1 << BT_CLK27_SEL_BIT) |
				 /* xclk27 is input. */
				(1 << BT_XCLK27_EN_BIT));
		/* bt656_wr(VDIN_WR_V_START_END, 239 |   */
		/* (0 << 16));  */
	}
}

/* CAMERA input(progressive mode): CLOCK + D0~D7 + HREF + VSYNC */
static void reinit_camera_dec(struct am656in_dev_s *devp)
{
	unsigned int offset = devp->index;

	/* reset_bt656in_module(); */
	unsigned int temp_data;
	unsigned char hsync_enable = devp->para.hsync_phase;
	unsigned char vsync_enable = devp->para.vsync_phase;
	unsigned short hs_bp       = devp->para.hs_bp;
	unsigned short vs_bp       = devp->para.vs_bp;
	unsigned int h_active      = devp->para.h_active;
	unsigned int v_active      = devp->para.v_active;

	/* disable 656,reset */
	bt656_wr(offset, BT_CTRL, 1 << 31);

	/*wr(BT_VIDEOSTART, 1 | (1 << 16));
	 *  //Line number of the first video start line in field 0/1.
	 *    there is a blank
	 * wr(BT_VIDEOEND , (am656in_dec_info.active_line ));
	 *  //Line number of the last video line in field 1.
	 *    added video end for avoid overflow.
	 * ((am656in_dec_info.active_line ) << 16));
	 */
	/* Line number of the last video line in field 0 */

	/* use external idq pin. */
	bt656_wr(offset, BT_PORT_CTRL, (0 << BT_IDQ_EN)   |
			(0 << BT_IDQ_PHASE)   |
			 /* FID came from HS VS. */
			(0 << BT_FID_HSVS)    |
			(1 << BT_ACTIVE_HMODE) |
			(vsync_enable << BT_VSYNC_PHASE) |
			(hsync_enable << BT_HSYNC_PHASE) |
			(0 << BT_D8B)         |
			(4 << BT_FID_DELAY)   | /* 4 */
			(0 << BT_VSYNC_DELAY)  |
			(2 << BT_HSYNC_DELAY)); /* 2 */

	/* WRITE_CBUS_REG(BT_PORT_CTRL,0x421001); */
	/* FID field check done point. */
	bt656_wr(offset, BT_601_CTRL2, (10 << 16));

	bt656_wr(offset, BT_SWAP_CTRL,
		 (5 << 0) |        /* POS_Cb0_IN */
		 (4 << 4) |        /* POS_Y0_IN */
		 (7 << 8) |        /* POS_Cr0_IN */
		 (6 << 12));       /* POS_Y1_IN */

	/* horizontal active data start offset, *2 for 422 sampling */
	bt656_wr(offset, BT_LINECTRL, (1 << 31) |
			(((h_active + hs_bp) << 1) << 16) |
			(hs_bp << 1));/* horizontal active data start offset */

	/* ANCI is the field blanking data, like close caption.
	 * If it connected to digital camera interface,
	 *  the jpeg bitstream also use this ANCI FIFO.
	 */
	/* bt656_wr(BT_ANCISADR, devp->mem_start); */
	/* bt656_wr(BT_ANCIEADR, 0);//devp->mem_start + devp->mem_size); */

	bt656_wr(offset, BT_AFIFO_CTRL,   (1 << 31) |
			(1 << 6) |     /* fill _en; */
			(1 << 3));     /* urgent */

	bt656_wr(offset, BT_INT_CTRL,   /* (1 << 5) |    //ancififo done int. */
			/* (1 << 4) |    //SOF interrupt enable. */
			/* (1 << 3) |      //EOF interrupt enable. */
			(1 << 1));      /* input overflow interrupt enable. */
	/* (1 << 0));      //bt656 controller error interrupt enable. */

	 /* total lines per frame and total pixel per line */
	bt656_wr(offset, BT_ERR_CNT, ((2000) << 16) | (2000 * 10));
	/* otherwise there is always error flag, */
	/* because the camera input use HREF ont HSYNC, */
	/* there are some lines without HREF sometime */

	/* field 0/1 start lcnt */
	bt656_wr(offset, BT_FIELDSADR, (1 << 16) | 1);

	/* field 0/1 VBI last line number */
	bt656_wr(offset, BT_VBISTART, 1 | (1 << 16));
	/* field 0/1 VBI last line number */
	bt656_wr(offset, BT_VBIEND,   1 | (1 << 16));

	/* Line number of the first video start
	 * line in field 0/1.there is a blank
	 */
	bt656_wr(offset, BT_VIDEOSTART, vs_bp | (vs_bp << 16));
	/* Line number of the last video line in field 1.
	 *  added video end for avoid overflow.
	 */
	/* Line number of the last video line in field 0 */
	bt656_wr(offset, BT_VIDEOEND,
		 ((v_active + vs_bp - 1) |
		 ((v_active + vs_bp - 1) << 16)));

	bt656_wr(offset, BT_DELAY_CTRL, 0x22222222);

	/* enable BTR656 interface */
	if (devp->para.isp_fe_port == TVIN_PORT_CAMERA) {
		temp_data = (1 << BT_EN_BIT) /* enable BT module. */
			/* timing reference is from bit stream. */
			| (0 << BT_REF_MODE_BIT)
			| (0 << BT_FMT_MODE_BIT)      /* PAL */
			| (0 << BT_SLICE_MODE_BIT)   /* no ancillary flag. */
			/* BT656 standard interface. */
			| (0 << BT_MODE_BIT)
			| (1 << BT_CLOCK_ENABLE)	/* enable 656 clock. */
			| (0 << BT_FID_EN_BIT)	/* use external fid pin. */
			/* xclk27 is input. change to Raw_mode setting from M8*/
			/* |(0 << BT_XCLK27_EN_BIT)      */
			| (0 << BT_PROG_MODE)
			| (0 << BT_AUTO_FMT)
			| (0 << BT_CAMERA_MODE)     /* enable camera mode */
			| (1 << BT_656CLOCK_RESET)
			| (1 << BT_SYSCLOCK_RESET)
			| (1 << BT_RAW_MODE) /* enable raw data output */
			| (1 << BT_RAW_ISP) /* enable raw data to isp */
			| (0 << 28) /* enable csi2 pin */
		    ;
	} else {
		temp_data =
			/* BT656 standard interface. */
			(0 << BT_MODE_BIT)
			/* camera mode */
			| (1 << BT_CAMERA_MODE)
			/* enable BT data input */
			| (1 << BT_EN_BIT)
			/* timing reference is from bit stream. */
			| (0 << BT_REF_MODE_BIT)
			| (0 << BT_FMT_MODE_BIT)     /* NTSC */
			| (0 << BT_SLICE_MODE_BIT)
			/* use external fid pin. */
			| (0 << BT_FID_EN_BIT)
			/* enable bt656 clock input */
			| (1 << 7)
			| (0 << 18)              /* eol_delay //4 */
			| (1 << 16) /* update status for debug */
			/* enable sys clock input */
			| (1 << BT_XCLK27_EN_BIT)
			| (0x60000000);   /* out of reset for BT_CTRL. */
	}
	if (devp->para.bt_path == BT_PATH_GPIO ||
	    devp->para.bt_path == BT_PATH_GPIO_B) {
		temp_data &= (~(1 << 28));
		bt656_wr(offset, BT_CTRL, temp_data);
	} else if (devp->para.bt_path == BT_PATH_CSI2) {
#ifdef CONFIG_TVIN_ISP
		temp_data |= (1 << 28);
		bt656_wr(offset, BT_CTRL, temp_data);
		/* power on mipi csi phy */
		aml_write_cbus(HHI_CSI_PHY_CNTL0, 0xfdc1ff81);
		aml_write_cbus(HHI_CSI_PHY_CNTL1, 0x3fffff);
		temp_data = aml_read_cbus(HHI_CSI_PHY_CNTL2);
		temp_data &= 0x7ff00000;
		temp_data |= 0x80000fc0;
		aml_write_cbus(HHI_CSI_PHY_CNTL2, temp_data);
#endif
	}
}

/* bt601_hdmi input(progressive or interlace): CLOCK + D0~D7 + HSYNC + VSYNC */
static void reinit_bt601_hdmi_dec(struct am656in_dev_s *devp)
{
	unsigned int offset = devp->index;

	/* reset_bt656in_module(); */
	unsigned int temp_data;
	unsigned char hsync_enable = devp->para.hsync_phase;
	unsigned char vsync_enable = devp->para.vsync_phase;
	unsigned short hs_bp       = devp->para.hs_bp;
	unsigned short vs_bp       = devp->para.vs_bp;
	unsigned int h_active      = devp->para.h_active;
	unsigned int v_active      = devp->para.v_active;
	unsigned int fid_check_cnt = devp->para.fid_check_cnt;

	/* disable 656,reset */
	bt656_wr(offset, BT_CTRL, 1 << 31);

	bt656_wr(offset, BT_PORT_CTRL, (0 << BT_IDQ_EN)   |
			(0 << BT_IDQ_PHASE)     |
			(1 << BT_ACTIVE_HMODE)  |
			(vsync_enable << BT_VSYNC_PHASE) |
			(hsync_enable << BT_HSYNC_PHASE) |
			(0 << BT_D8B)           |
			(0 << BT_FID_DELAY)     |
			(0 << BT_FID_HSVS_PCNT) | /* use hs/vs */
			(1 << BT_FID_HSVS)      | /* generate fid */
			(0 << BT_VSYNC_DELAY)   |
			(0 << BT_HSYNC_DELAY));

	/* FID field check done point. */
	bt656_wr(offset, BT_601_CTRL2, (fid_check_cnt << 16));

	bt656_wr(offset, BT_SWAP_CTRL,
		 (4 << 0) |        /* POS_Cb0_IN */
		 (5 << 4) |        /* POS_Y0_IN */
		 (6 << 8) |        /* POS_Cr0_IN */
		 (7 << 12));       /* POS_Y1_IN */

	/* horizontal active data start offset, *2 for 422 sampling */
	bt656_wr(offset, BT_LINECTRL, (1 << 31) |
			(((h_active + hs_bp) << 1) << 16) |
			(hs_bp << 1));

	bt656_wr(offset, BT_AFIFO_CTRL,   (1 << 31) |
			(1 << 6) |     /* fill _en; */
			(1 << 3));     /* urgent */

	bt656_wr(offset, BT_INT_CTRL,    /* (1 << 5) |   //ancififo done int. */
			/* (1 << 4) |    //SOF interrupt enable. */
			/* (1 << 3) |      //EOF interrupt enable. */
			(1 << 1));      /* input overflow interrupt enable. */
		/* (1 << 0));      //bt656 controller error interrupt enable. */

	 /* total lines per frame and total pixel per line */
	bt656_wr(offset, BT_ERR_CNT, ((2000) << 16) | (2000 * 10));
	/* otherwise there is always error flag, */
	/* because the camera input use HREF ont HSYNC, */
	/* there are some lines without HREF sometime */

	/* field 0/1 start lcnt */
	bt656_wr(offset, BT_FIELDSADR, (0 << 16) | 0);

	/* field 0/1 VBI last line number */
	bt656_wr(offset, BT_VBISTART, 0 | (0 << 16));
	/* field 0/1 VBI last line number */
	bt656_wr(offset, BT_VBIEND,   0 | (0 << 16));

	/* Line number of the first video start
	 * line in field 0/1.there is a blank
	 */
	bt656_wr(offset, BT_VIDEOSTART, vs_bp | (vs_bp << 16));
	/* Line number of the last video line in field 1.
	 *  added video end for avoid overflow.
	 */
	/* Line number of the last video line in field 0 */
	bt656_wr(offset, BT_VIDEOEND,
		 ((v_active + vs_bp - 1) |
		 ((v_active + vs_bp - 1) << 16)));

	bt656_wr(offset, BT_DELAY_CTRL, 0x22222222);

	/* enable BTR656 interface */
	temp_data =
		/* BT656 standard interface. */
		(0 << BT_MODE_BIT)
		/* camera mode */
		| (1 << BT_CAMERA_MODE)
		/* enable BT data input */
		| (1 << BT_EN_BIT)
		/* timing reference is from bit stream. */
		| (0 << BT_REF_MODE_BIT)
		| (0 << BT_FMT_MODE_BIT)
		| (0 << BT_SLICE_MODE_BIT)
		/* use external fid pin. */
		| (0 << BT_FID_EN_BIT)
		/* enable bt656 clock input */
		| (1 << 7)
		| (0 << 18)              /* eol_delay */
		| (1 << 16) /* update status for debug */
		/* enable sys clock input */
		| (1 << BT_XCLK27_EN_BIT)
		| (0x60000000);   /* out of reset for BT_CTRL. */

	temp_data &= (~(1 << 28));
	bt656_wr(offset, BT_CTRL, temp_data);
}

static void start_amvdec_656_601_camera_in(struct am656in_dev_s *devp)
{
	enum tvin_port_e port =  devp->para.port;

	if (devp->dec_status & TVIN_AM656_RUNNING) {
		BT656PR("%s: bt656 have started already.\n", __func__);
		return;
	}
	devp->dec_status = TVIN_AM656_RUNNING;
	/* NTSC or PAL input(interlace mode): D0~D7(with SAV + EAV ) */
	if (port == TVIN_PORT_BT656) {
		devp->para.fmt = TVIN_SIG_FMT_BT656IN_576I_50HZ;
		init_656in_dec_parameter(devp);
		reinit_bt656in_dec(devp);
		/* reset_656in_dec_parameter(); */
		devp->dec_status = TVIN_AM656_RUNNING;
	} else if (port == TVIN_PORT_BT601) {
		devp->para.fmt = TVIN_SIG_FMT_BT601IN_576I_50HZ;
		init_656in_dec_parameter(devp);
		reinit_bt601in_dec(devp);
		devp->dec_status = TVIN_AM656_RUNNING;

	} else if (port == TVIN_PORT_CAMERA) {
		init_656in_dec_parameter(devp);
		reinit_camera_dec(devp);
		devp->dec_status = TVIN_AM656_RUNNING;
	} else if (port == TVIN_PORT_BT656_HDMI) {
		init_656in_dec_hdmi_parameter(devp);
		reinit_bt601_hdmi_dec(devp);
		devp->dec_status = TVIN_AM656_RUNNING;
	} else if (port == TVIN_PORT_BT601_HDMI) {
		init_656in_dec_hdmi_parameter(devp);
		reinit_bt601_hdmi_dec(devp);
		devp->dec_status = TVIN_AM656_RUNNING;
	} else {
		devp->para.fmt  = TVIN_SIG_FMT_NULL;
		devp->para.port = TVIN_PORT_NULL;
		BT656ERR("%s: input is not selected, please try again.\n",
			 __func__);
		return;
	}
	BT656PR("bt656[%d](%s): %s input port: %s fmt: %s.\n",
		devp->index, BT656_VER, __func__,
		tvin_port_str(devp->para.port),
		tvin_sig_fmt_str(devp->para.fmt));
}

static void stop_amvdec_bt656(struct am656in_dev_s *devp)
{
	if (devp->dec_status & TVIN_AM656_RUNNING) {
		reset_bt656in_module(devp);
		devp->dec_status = TVIN_AM656_STOP;
	} else {
		BT656PR("bt656[%d]:%s device is not started yet.\n",
			devp->index, __func__);
	}
}

static void am656_clktree_control(struct am656in_dev_s *devp, int flag)
{
	BT656PR("bt656[%d]:%s: flag=%d\n", devp->index, __func__, flag);
	if (flag) { /* clk enable */
		if (IS_ERR(devp->gate_bt656)) {
			BT656ERR("bt656[%d] %s: gate_bt656\n",
				 devp->index, __func__);
		} else {
			clk_prepare_enable(devp->gate_bt656);
		}
		if (IS_ERR(devp->gate_bt656_pclk)) {
			BT656ERR("bt656[%d] %s: gate_bt656_pclk\n",
				 devp->index, __func__);
		} else {
			clk_prepare_enable(devp->gate_bt656_pclk);
		}
		if (IS_ERR(devp->bt656_clk)) {
			BT656ERR("bt656[%d] %s: bt656_clk\n",
				 devp->index, __func__);
		} else {
			clk_prepare_enable(devp->bt656_clk);
		}
		usleep_range(100, 150);
	} else { /* clk disable */
		if (IS_ERR(devp->bt656_clk)) {
			BT656ERR("bt656[%d] %s: bt656_clk\n",
				 devp->index, __func__);
		} else {
			clk_disable_unprepare(devp->bt656_clk);
		}
		if (IS_ERR(devp->gate_bt656_pclk)) {
			BT656ERR("bt656[%d] %s: gate_bt656_pclk\n",
				 devp->index, __func__);
		} else {
			clk_disable_unprepare(devp->gate_bt656_pclk);
		}
		if (IS_ERR(devp->gate_bt656)) {
			BT656ERR("bt656[%d] %s: gate_bt656\n",
				 devp->index, __func__);
		} else {
			clk_disable_unprepare(devp->gate_bt656);
		}
	}
}

/*
 * return true when need skip frame otherwise return false
 */
static bool am656_check_skip_frame(struct tvin_frontend_s *fe)
{
	struct am656in_dev_s *devp;

	devp =  container_of(fe, struct am656in_dev_s, frontend);
	if (devp->skip_vdin_frame_count > 0) {
		devp->skip_vdin_frame_count--;
		return true;
	} else {
		return false;
	}
}

int am656in_support(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
	if (port < TVIN_PORT_BT656 || port > TVIN_PORT_BT601_HDMI)
		return -1;
	else
		return 0;
}

static int am656in_open(struct inode *node, struct file *file)
{
	struct am656in_dev_s *devp;

	/* Get the per-device structure that contains this cdev */
	devp = container_of(node->i_cdev, struct am656in_dev_s, cdev);

	if (devp->index >= hw_cnt)
		return -ENXIO;

	file->private_data = devp;

	BT656PR("open device %s ok\n", dev_name(devp->dev));

	return 0;
}

static int am656in_release(struct inode *node, struct file *file)
{
	/* struct vdin_dev_s *devp = file->private_data; */

	file->private_data = NULL;

	/* BT656PR("close device %s ok\n", dev_name(devp->dev)); */

	return 0;
}

static const struct file_operations am656in_fops = {
	.owner    = THIS_MODULE,
	.open     = am656in_open,
	.release  = am656in_release,
};

/*called by vdin && sever for v4l2 framework*/
void am656in_start(struct tvin_frontend_s *fe, enum tvin_sig_fmt_e fmt)
{
	struct am656in_dev_s *devp;

	devp = container_of(fe, struct am656in_dev_s, frontend);

	mutex_lock(&bt656_mutex);
	start_amvdec_656_601_camera_in(devp);
	mutex_unlock(&bt656_mutex);
}

static void am656in_stop(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
	struct am656in_dev_s *devp;

	devp = container_of(fe, struct am656in_dev_s, frontend);
	if (port < TVIN_PORT_BT656 || port > TVIN_PORT_BT601_HDMI) {
		BT656ERR("%s: invalid port %d.\n", __func__, port);
		return;
	}

	mutex_lock(&bt656_mutex);
	stop_amvdec_bt656(devp);
	BT656PR("bt656[%d] %s stop device stop ok.\n", devp->index, __func__);
	mutex_unlock(&bt656_mutex);
}

static void am656in_get_sig_property(struct tvin_frontend_s *fe,
				     struct tvin_sig_property_s *prop)
{
	struct am656in_dev_s *devp;

	devp = container_of(fe, struct am656in_dev_s, frontend);
	prop->color_format = devp->para.cfmt;
	prop->dest_cfmt    = devp->para.dfmt;
	prop->decimation_ratio = 0;
}

/*as use the spin_lock,
 *1--there is no sleep,
 *2--it is better to shorter the time,
 */
int am656in_isr(struct tvin_frontend_s *fe, unsigned int hcnt)
{
	unsigned int ccir656_status = 0;
	struct am656in_dev_s *devp;
	unsigned int offset;

	devp = container_of(fe, struct am656in_dev_s, frontend);
	offset = devp->index;
	ccir656_status = bt656_rd(offset, BT_STATUS);
	if (ccir656_status & 0xf0)   /* AFIFO OVERFLOW */
		devp->overflow_cnt++;
	if (devp->overflow_cnt > 5) {
		devp->overflow_cnt = 0;
		 /* NTSC or PAL input(interlace mode): D0~D7(with SAV + EAV ) */
		if (devp->para.port == TVIN_PORT_BT656)
			reinit_bt656in_dec(devp);
		else if (devp->para.port == TVIN_PORT_BT601)
			reinit_bt601in_dec(devp);
		else if (devp->para.port == TVIN_PORT_BT656_HDMI)
			reinit_bt601_hdmi_dec(devp);
		else if (devp->para.port == TVIN_PORT_BT601_HDMI)
			reinit_bt601_hdmi_dec(devp);
		else /* if(am656in_dec_info.para.port == TVIN_PORT_CAMERA) */
			reinit_camera_dec(devp);
		 /* WRITE_CBUS_REGite 1 to clean the SOF interrupt bit */
		bt656_wr(offset, BT_STATUS, ccir656_status | (1 << 9));
		BT656PR("bt656[%d] %s bt656in fifo overflow.\n",
			devp->index, __func__);
	}
	return 0;
}

/*
 *power on 656 module&init the parameters,such as
 *power color fmt...,will be used by vdin
 */
static int am656in_feopen(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
	struct am656in_dev_s *devp;
	struct vdin_parm_s *parm = fe->private_data;

	devp = container_of(fe, struct am656in_dev_s, frontend);

	if (port < TVIN_PORT_BT656 || port > TVIN_PORT_BT601_HDMI) {
		BT656ERR("bt656[%d] %s:invalid port %d.\n",
			 devp->index, __func__, port);
		return -1;
	}
	if (port == TVIN_PORT_CAMERA)
		devp->skip_vdin_frame_count = parm->skip_count;

	/*copy the param from vdin to bt656*/
	if (!memcpy(&devp->para, parm, sizeof(struct vdin_parm_s))) {
		BT656ERR("bt656[%d] %s memcpy error\n", devp->index, __func__);
		return -1;
	}
	/*avoidint the param port is't equal with port*/
	devp->para.port = port;
	BT656PR("bt656[%d]:%s: color fmt:%s, hs phase:%u, vs phase:%u\n",
		devp->index, __func__,
		tvin_color_fmt_str(parm->cfmt),
		parm->hsync_phase, parm->vsync_phase);
	BT656PR("frame rate:%u, hs_bp:%u, vs_bp:%u\n",
		parm->frame_rate, parm->hs_bp, parm->vs_bp);

	/* bt656 clock enable */
	am656_clktree_control(devp, 1);

	return 0;
}

/*
 *power off the 656 module,clear the parameters
 */
static void am656in_feclose(struct tvin_frontend_s *fe)
{
	struct am656in_dev_s *devp = NULL;
	enum tvin_port_e port = 0;

	devp = container_of(fe, struct am656in_dev_s, frontend);
	port = devp->para.port;

	if (port < TVIN_PORT_BT656 || port > TVIN_PORT_BT601_HDMI) {
		BT656ERR("bt656[%d] %s:invalid port %d.\n",
			 devp->index, __func__, port);
		return;
	}

	memset(&devp->para, 0, sizeof(struct vdin_parm_s));

	/* bt656 clock disable */
	am656_clktree_control(devp, 0);
}

static struct tvin_state_machine_ops_s am656_machine_ops = {
	.nosig               = NULL,
	.fmt_changed         = NULL,
	.get_fmt             = NULL,
	.fmt_config          = NULL,
	.adc_cal             = NULL,
	.pll_lock            = NULL,
	.get_sig_property    = am656in_get_sig_property,
	.vga_set_param       = NULL,
	.vga_get_param       = NULL,
	.check_frame_skip    = am656_check_skip_frame,
};

static struct tvin_decoder_ops_s am656_decoder_ops_s = {
	.support                = am656in_support,
	.open                   = am656in_feopen,
	.start                  = am656in_start,
	.stop                   = am656in_stop,
	.close                  = am656in_feclose,
	.decode_isr             = am656in_isr,
};

#ifdef CONFIG_OF
struct meson_bt656in_data meson_g12a_bt656in_data = {
	.cpu_id = BT656_CPU_TYPE_G12A,
	.name = "meson-g12a-bt656in",
};

struct meson_bt656in_data meson_g12b_bt656in_data = {
	.cpu_id = BT656_CPU_TYPE_G12B,
	.name = "meson-g12b-bt656in",
};

#ifndef CONFIG_AMLOGIC_REMOVE_OLD
struct meson_bt656in_data meson_tl1_bt656in_data = {
	.cpu_id = BT656_CPU_TYPE_TL1,
	.name = "meson-tl1-bt656in",
};
#endif

struct meson_bt656in_data meson_sm1_bt656in_data = {
	.cpu_id = BT656_CPU_TYPE_SM1,
	.name = "meson-sm1-bt656in",
};

struct meson_bt656in_data meson_tm2_bt656in_data = {
	.cpu_id = BT656_CPU_TYPE_TM2,
	.name = "meson-tm2-bt656in",
};

struct meson_bt656in_data meson_t3_bt656in_data = {
	.cpu_id = BT656_CPU_TYPE_T3,
	.name = "meson-t3-bt656in",
};

static const struct of_device_id meson_bt656in_dt_match[] = {
	{
		.compatible = "amlogic, bt656in-g12a",
		.data		= &meson_g12a_bt656in_data,
	}, {
		.compatible = "amlogic, bt656in-g12b",
		.data		= &meson_g12b_bt656in_data,
	},
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
	{
		.compatible = "amlogic, bt656in-tl1",
		.data		= &meson_tl1_bt656in_data,
	},
#endif
	{
		.compatible = "amlogic, bt656in-sm1",
		.data		= &meson_sm1_bt656in_data,
	}, {
		.compatible = "amlogic, bt656in-tm2",
		.data		= &meson_tm2_bt656in_data,
	}, {
		.compatible = "amlogic, bt656in-t3",
		.data		= &meson_t3_bt656in_data,
	},
	{},
};
#endif

static int bt656_add_cdev(struct cdev *cdevp,
			  const struct file_operations *fops, int minor)
{
	int ret;
	dev_t devno = MKDEV(MAJOR(am656in_devno), minor);

	cdev_init(cdevp, fops);
	cdevp->owner = THIS_MODULE;
	ret = cdev_add(cdevp, devno, 1);
	return ret;
}

static struct device *bt656_create_device(struct device *parent, int minor)
{
	dev_t devno = MKDEV(MAJOR(am656in_devno), minor);

	return device_create(am656in_clsp, parent, devno, NULL, "%s%d",
			BT656_DEV_NAME, minor);
}

static void bt656_delete_device(int minor)
{
	dev_t devno = MKDEV(MAJOR(am656in_devno), minor);

	device_destroy(am656in_clsp, devno);
}

static int amvdec_656in_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct am656in_dev_s *devp = NULL;
	const struct of_device_id *match;
	int bt656_rate;
	struct resource *res = 0;
	int size = 0;
	int index = 0;
	struct device_node *child;
	const char *str;
	char bt656_clk_name[20];

	BT656PR("%s: start probe .\n", __func__);

	am_bt656_data = &bt656_data;
	match = of_match_device(meson_bt656in_dt_match, &pdev->dev);
	if (!match) {
		BT656PR("%s,no matched table\n", __func__);
		return -1;
	}

	am_bt656_data = (struct meson_bt656in_data *)match->data;
	BT656PR("%s, cpu_id:%d,name:%s\n", __func__,
		am_bt656_data->cpu_id, am_bt656_data->name);

	if (bt656_cpu_type() == BT656_CPU_TYPE_G12A ||
	    bt656_cpu_type() == BT656_CPU_TYPE_G12B ||
	    bt656_cpu_type() == BT656_CPU_TYPE_TL1  ||
	    bt656_cpu_type() == BT656_CPU_TYPE_SM1  ||
	    bt656_cpu_type() == BT656_CPU_TYPE_TM2  ||
	    bt656_cpu_type() == BT656_CPU_TYPE_T3) {
		hw_cnt = 1;
	} else {
		hw_cnt = 0;
	}

	if (hw_cnt == 0) {
		BT656ERR("no bt656 support for current chip\n");
		return -EFAULT;
	}
	BT656PR("%s. %d hardware detected!.\n", __func__, hw_cnt);

	mutex_init(&bt656_mutex);

	ret = alloc_chrdev_region(&am656in_devno, 0, hw_cnt, BT656_DEV_NAME);
	if (ret < 0) {
		BT656ERR("%s: failed to alloc major number\n", __func__);
		goto fail_alloc_cdev_region;
	}
	BT656PR("%s: major %d\n", __func__, MAJOR(am656in_devno));

	am656in_clsp = class_create(THIS_MODULE, BT656_CLS_NAME);
	if (IS_ERR(am656in_clsp)) {
		ret = PTR_ERR(am656in_clsp);
		BT656ERR("%s: failed to create class\n", __func__);
		goto fail_class_create;
	}

	for_each_child_of_node(pdev->dev.of_node, child) {
		ret = of_property_read_u32(child, "bt656_id", &index);
		if (ret) {
			BT656ERR("%s: don't find  bt656 id.\n", __func__);
			goto fail_kmalloc_dev;
		}
		if (index == 0 || index > 2) {
			BT656ERR("%s: invalid bt656 index %d.\n",
				 __func__, index);
			goto fail_kmalloc_dev;
		}

		ret = of_property_read_string(child, "status", &str);
		if (ret) {
			BT656ERR("%s: bt656[%d] don't find status, disabled\n",
				 __func__, index);
			continue;
		} else {
			if (strncmp(str, "okay", 2))
				continue;
		}

		/* malloc dev */
		am656in_devp[index - 1] =
			kmalloc(sizeof(struct am656in_dev_s), GFP_KERNEL);
		if (!am656in_devp[index - 1]) {
			BT656ERR("%s: devp malloc error\n", __func__);
			goto fail_kmalloc_dev;
		}
		memset(am656in_devp[index - 1], 0,
		       sizeof(struct am656in_dev_s));

		devp = am656in_devp[index - 1];
		devp->index = index;
		BT656PR("%s: bt656 devp->index =%d\n", __func__, devp->index);

		/* create cdev and register with sysfs */
		ret = bt656_add_cdev(&devp->cdev, &am656in_fops, devp->index);
		if (ret) {
			BT656ERR("%s: failed to add cdev\n", __func__);
			goto fail_add_cdev;
		}
		devp->dev = bt656_create_device(&pdev->dev, devp->index);
		if (IS_ERR(devp->dev)) {
			BT656ERR("%s: failed to create device\n", __func__);
			ret = PTR_ERR(devp->dev);
			goto fail_create_device;
		}

		ret = device_create_file(devp->dev, &dev_attr_reg);
		if (ret < 0) {
			BT656ERR("fail to create dbg attribute file\n");
			goto fail_create_file;
		}

		/* set clktree */
		sprintf(bt656_clk_name, "cts_bt656_clk%d", devp->index);
		devp->bt656_clk = clk_get(&pdev->dev, bt656_clk_name);
		clk_set_rate(devp->bt656_clk, 333333333);
		if (!IS_ERR(devp->bt656_clk)) {
			bt656_rate = clk_get_rate(devp->bt656_clk);
			/*clk_put(devp->bt656_clk);*/
			BT656PR("%s: bt656[%d] clock is %d MHZ\n",
				__func__,
				devp->index, bt656_rate / 1000000);
		} else {
			BT656ERR("%s: bt656[%d] cannot get %s !!!\n",
				 __func__, devp->index, bt656_clk_name);
			ret = -ENOENT;
			goto fail_get_clktree;
		}

		devp->gate_bt656 = clk_get(&pdev->dev, "clk_gate_bt656");
		if (IS_ERR(devp->gate_bt656)) {
			BT656ERR("%s: cannot get clk_gate_bt656 !!!\n",
				 __func__);
		}

		devp->gate_bt656_pclk = clk_get(&pdev->dev,
						"clk_gate_bt656_pclk1");
		if (IS_ERR(devp->gate_bt656_pclk)) {
			BT656ERR("%s: cannot get clk_gate_bt656_pclk1 !!!\n",
				 __func__);
		}
		/* set regmap */
		BT656PR("%s: bt656[%d] start get ioremap .\n",
			__func__, devp->index);
		res = platform_get_resource(pdev, IORESOURCE_MEM,
					    (devp->index - 1));
		if (!res) {
			BT656ERR("missing memory resource\n");
			ret = -ENOMEM;
			goto fail_get_clktree;
		}

		size = resource_size(res);
		bt656_reg_base[devp->index - 1] =
			devm_ioremap(&pdev->dev, res->start, size);
		if (!bt656_reg_base[devp->index - 1]) {
			BT656ERR("bt656[%d] ioremap failed\n", devp->index);
			ret = -ENOMEM;
			goto fail_get_clktree;
		}
		BT656PR("%s: bt656[%d] mapped reg_base=0x%p, size=0x%x\n",
			__func__, devp->index,
			bt656_reg_base[devp->index - 1], size);

		/* set drvdata */
		dev_set_drvdata(devp->dev, devp);

		/*register frontend */
		sprintf(devp->frontend.name, "%s%d",
			BT656_DEV_NAME, devp->index);
		/* tvin_frontend_init(&devp->frontend,
		 * &am656_decoder_ops_s, &am656_machine_ops, pdev->id);
		 */
		if (!tvin_frontend_init(&devp->frontend, &am656_decoder_ops_s,
					&am656_machine_ops, devp->index - 1)) {
			BT656PR("%s: tvin_frontend_init done :%d\n",
				__func__, devp->index);
			if (tvin_reg_frontend(&devp->frontend)) {
				BT656ERR("%s register frontend error\n",
					 __func__);
			}
		}
	}
	BT656PR("driver probe ok.\n");

	return ret;

/*
 *fail_get_resource_mem:
 *	devm_release_mem_region(&pdev->dev, res->start, size);
 */
fail_get_clktree:
	device_remove_file(devp->dev, &dev_attr_reg);
fail_create_file:
	bt656_delete_device(pdev->id);
fail_create_device:
	cdev_del(&devp->cdev);
fail_add_cdev:
	kfree(devp);
fail_kmalloc_dev:
	class_destroy(am656in_clsp);
fail_class_create:
	unregister_chrdev_region(am656in_devno, hw_cnt);
fail_alloc_cdev_region:
	return ret;
}

static int amvdec_656in_remove(struct platform_device *pdev)
{
	struct am656in_dev_s *devp;
	int index;

	for (index = 0; index < hw_cnt; index++) {
		devp = am656in_devp[index];
		if (!devp)
			continue;
		device_remove_file(devp->dev, &dev_attr_reg);
		tvin_unreg_frontend(&devp->frontend);
		bt656_delete_device(pdev->id);
		cdev_del(&devp->cdev);
		kfree((const void *)devp);
	}
	class_destroy(am656in_clsp);
	unregister_chrdev_region(am656in_devno, hw_cnt);

	return 0;
}

static int amvdec_656in_resume(struct platform_device *pdev)
{
	BT656PR("%s\n", __func__);
	return 0;
}

static int amvdec_656in_suspend(struct platform_device *pdev,
				pm_message_t state)
{
	struct am656in_dev_s *devp;
	int index;

	mutex_lock(&bt656_mutex);
	BT656PR("%s\n", __func__);
	for (index = 0; index < hw_cnt; index++) {
		devp = am656in_devp[index];
		if (!devp)
			continue;
		if (devp->dec_status & TVIN_AM656_RUNNING) {
			reset_bt656in_module(devp);
			devp->dec_status = TVIN_AM656_STOP;
			am656_clktree_control(devp, 0);
		}
	}
	mutex_unlock(&bt656_mutex);
	return 0;
}

static void amvdec_656in_shutdown(struct platform_device *pdev)
{
	struct am656in_dev_s *devp;
	int index;

	BT656PR("%s\n", __func__);
	for (index = 0; index < hw_cnt; index++) {
		devp = am656in_devp[index];
		if (!devp)
			continue;
		if (devp->dec_status & TVIN_AM656_RUNNING) {
			reset_bt656in_module(devp);
			devp->dec_status = TVIN_AM656_STOP;
			am656_clktree_control(devp, 0);
		}
	}
}

static struct platform_driver amvdec_656in_driver = {
	.probe  = amvdec_656in_probe,
	.remove = amvdec_656in_remove,
	.suspend = amvdec_656in_suspend,
	.resume = amvdec_656in_resume,
	.shutdown = amvdec_656in_shutdown,
	.driver	= {
		.name           = BT656_DRV_NAME,
		.owner          = THIS_MODULE,
#ifdef CONFIG_OF
		.of_match_table = meson_bt656in_dt_match,
#endif
	}
};

int __init amvdec_656in_init_module(void)
{
	int ret = 0;

	ret = platform_driver_register(&amvdec_656in_driver);
	if (ret != 0) {
		BT656ERR("%s: failed to register driver\n", __func__);
		return -1;
	}

	return 0;
}

void __exit amvdec_656in_exit_module(void)
{
	platform_driver_unregister(&amvdec_656in_driver);
}

//MODULE_DESCRIPTION("AMLOGIC BT656_601 input driver");
//MODULE_LICENSE("GPL");
//MODULE_VERSION("2.0.0");

