/*
 * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>

#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#include <asm/errno.h>
#include <asm/io.h>

#include <linux/string.h>
#include <linux/list.h>
#include <gis.h>

#include "mxc_vadc.h"

#define reg32_write(addr, val) __raw_writel(val, addr)
#define reg32_read(addr)       __raw_readl(addr)
#define reg32setbit(addr, bitpos) \
	reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos))))

#define reg32clrbit(addr, bitpos) \
	reg32_write((addr), (reg32_read((addr)) & (0xFFFFFFFF ^ (1<<(bitpos)))))

void __iomem *vafe_regbase;
void __iomem *vdec_regbase;

enum {
	STD_NTSC = 0,
	STD_PAL,
};

/* Video format structure. */
struct video_fmt_t{
	int v4l2_id;		/* Video for linux ID. */
	char name[16];		/* Name (e.g., "NTSC", "PAL", etc.) */
	u16 active_width;	/* Active width. */
	u16 active_height;	/* Active height. */
};

/* Description of video formats supported.
 *
 *  PAL: active=720x576.
 *  NTSC:active=720x480.
 */
static struct video_fmt_t video_fmts[] = {
	/* NTSC */
	{
	 .v4l2_id = STD_NTSC,
	 .name = "NTSC",
	 .active_width = 720,
	 .active_height = 480,
	 },
	/* (B, G, H, I, N) PAL */
	{
	 .v4l2_id = STD_PAL,
	 .name = "PAL",
	 .active_width = 720,
	 .active_height = 576,
	 },
};

static void afe_voltage_clampingmode(void)
{
	reg32_write(AFE_CLAMP, 0x07);
	reg32_write(AFE_CLMPAMP, 0x60);
	reg32_write(AFE_CLMPDAT, 0xF0);
}

static void afe_alwayson_clampingmode(void)
{
	reg32_write(AFE_CLAMP, 0x15);
	reg32_write(AFE_CLMPDAT, 0x08);
	reg32_write(AFE_CLMPAMP, 0x00);
}

static void afe_init(void)
{
	reg32_write(AFE_PDBUF, 0x1f);
	reg32_write(AFE_PDADC, 0x0f);
	reg32_write(AFE_PDSARH, 0x01);
	reg32_write(AFE_PDSARL, 0xff);
	reg32_write(AFE_PDADCRFH, 0x01);
	reg32_write(AFE_PDADCRFL, 0xff);
	reg32_write(AFE_ICTRL, 0x3a);
	reg32_write(AFE_ICTLSTG, 0x1e);

	reg32_write(AFE_RCTRLSTG, 0x1e);
	reg32_write(AFE_INPBUF, 0x035);
	reg32_write(AFE_INPFLT, 0x02);
	reg32_write(AFE_ADCDGN, 0x40);
	reg32_write(AFE_TSTSEL, 0x10);

	reg32_write(AFE_ACCTST, 0x07);

	reg32_write(AFE_BGREG, 0x08);

	reg32_write(AFE_ADCGN, 0x09);

	/* set current controlled clamping
	* always on, low current */
	reg32_write(AFE_CLAMP, 0x11);
	reg32_write(AFE_CLMPAMP, 0x08);
}

static void vdec_mode_timing_init(u32 std)
{
	if (std == STD_NTSC) {
		/* NTSC 720x480 */
		printf("NTSC\n");
		reg32_write(VDEC_HACTS, 0x66);
		reg32_write(VDEC_HACTE, 0x24);

		reg32_write(VDEC_VACTS, 0x29);
		reg32_write(VDEC_VACTE, 0x04);

		/* set V Position */
		reg32_write(VDEC_VRTPOS, 0x2);
	} else if (std == STD_PAL) {
		/* PAL 720x576 */
		printf("PAL\n");
		reg32_write(VDEC_HACTS, 0x66);
		reg32_write(VDEC_HACTE, 0x24);

		reg32_write(VDEC_VACTS, 0x29);
		reg32_write(VDEC_VACTE, 0x04);

		/* set V Position */
		reg32_write(VDEC_VRTPOS, 0x6);
	} else
		printf("Error not support video mode\n");

	/* set H Position */
	reg32_write(VDEC_HZPOS, 0x60);

	/* set H ignore start */
	reg32_write(VDEC_HSIGS, 0xf8);

	/* set H ignore end */
	reg32_write(VDEC_HSIGE, 0x18);
}

/*
* vdec_init()
* Initialises the VDEC registers
* Returns: nothing
*/
static void vdec_init(struct sensor_data *vadc)
{
	/* Get work mode PAL or NTSC
	 * delay 500ms wait vdec detect input format*/
	udelay(500*1000);
	vadc_get_std(vadc);

	vdec_mode_timing_init(vadc->std_id);

	/* vcr detect threshold high, automatic detections */
	reg32_write(VDEC_VSCON2, 0);

	reg32_write(VDEC_BASE + 0x110, 0x01);

	/* set the noramp mode on the Hloop PLL. */
	reg32_write(VDEC_BASE+(0x14*4), 0x10);

	/* set the YC relative delay.*/
	reg32_write(VDEC_YCDEL, 0x90);

	/* setup the Hpll */
	reg32_write(VDEC_BASE+(0x13*4), 0x13);

	/* setup the 2d comb */
	/* set the gain of the Hdetail output to 3
	 * set the notch alpha gain to 1 */
	reg32_write(VDEC_CFC2, 0x34);

	/* setup various 2d comb bits.*/
	reg32_write(VDEC_BASE+(0x02*4), 0x01);
	reg32_write(VDEC_BASE+(0x03*4), 0x18);
	reg32_write(VDEC_BASE+(0x04*4), 0x34);

	/* set the start of the burst gate */
	reg32_write(VDEC_BRSTGT, 0x30);

	/* set 1f motion gain */
	reg32_write(VDEC_BASE+(0x0f*4), 0x20);

	/* set the 1F chroma motion detector thresh for colour reverse detection */
	reg32_write(VDEC_THSH1, 0x02);
	reg32_write(VDEC_BASE+(0x4a*4), 0x20);
	reg32_write(VDEC_BASE+(0x4b*4), 0x08);

	reg32_write(VDEC_BASE+(0x4c*4), 0x08);

	/* set the threshold for the narrow/wide adaptive chroma BW */
	reg32_write(VDEC_BASE+(0x20*4), 0x20);

	/* turn up the colour with the new colour gain reg */
	/* hue: */
	reg32_write(VDEC_HUE, 0x00);

	/* cbgain: 22 B4 */
	reg32_write(VDEC_CBGN, 0xb4);
	/* cr gain 80 */
	reg32_write(VDEC_CRGN, 0x80);
	/* luma gain (contrast) */
	reg32_write(VDEC_CNTR, 0x80);

	/* setup the signed black level register, brightness */
	reg32_write(VDEC_BRT, 0x00);

	/* filter the standard detection
	 * enable the comb for the ntsc443 */
	reg32_write(VDEC_STDDBG, 0x23);

	/* setup chroma kill thresh for no chroma */
	reg32_write(VDEC_CHBTH, 0x0);

	/* set chroma loop to wider BW
	 * no set it to normal BW. i fixed the bw problem.*/
	reg32_write(VDEC_YCDEL, 0x00);

	/* set the compensation in the chroma loop for the Hloop
	 * set the ratio for the nonarithmetic 3d comb modes.*/
	reg32_write(VDEC_BASE + (0x1d*4), 0x90);

	/* set the threshold for the nonarithmetic mode for the 2d comb
	 * the higher the value the more Fc Fh offset we will tolerate before turning off the comb. */
	reg32_write(VDEC_BASE + (0x33*4), 0xa0);

	/* setup the bluescreen output colour */
	reg32_write(VDEC_BASE + (0x3d*4), 35);
	reg32_write(VDEC_BLSCRCR, 114);
	reg32_write(VDEC_BLSCRCB, 212);

	/* disable the active blanking */
	reg32_write(VDEC_BASE + (0x15*4), 0x02);

	/* setup the luma agc for automatic gain. */
	reg32_write(VDEC_LMAGC2, 0x5e);
	reg32_write(VDEC_BASE + (0x40*4), 0x81);

	/* setup chroma agc */
	reg32_write(VDEC_CHAGC2, 0xa0);
	reg32_write(VDEC_CHAGC1, 0x01);

	/* setup the MV thresh lower nibble
	 * setup the sync top cap, upper nibble */
	reg32_write(VDEC_BASE + (0x3a*4), 0x80);
	reg32_write(VDEC_SHPIMP, 0x00);

	/* setup the vsync block */
	reg32_write(VDEC_VSCON1, 0x87);

	/* set the nosignal threshold
	 * set the vsync threshold */
	reg32_write(VDEC_VSSGTH, 0x35);

	/* set length for min hphase filter (or saturate limit if saturate is chosen) */
	reg32_write(VDEC_BASE + (0x45*4), 0x40);

	/* enable the internal resampler,
	 * select min filter not saturate for hphase noise filter for vcr detect.
	 * enable vcr pause mode different field lengths */
	reg32_write(VDEC_BASE + (0x46*4), 0x90);

	/* disable VCR detection, lock to the Hsync rather than the Vsync */
	reg32_write(VDEC_VSCON2, 0x04);

	/* set tiplevel goal for dc clamp. */
	reg32_write(VDEC_BASE + (0x3c*4), 0xB0);

	/* override SECAM detection and force SECAM off */
	reg32_write(VDEC_BASE + (0x2f*4), 0x20);

	/* Set r3d_hardblend in 3D control2 reg */
	reg32_write(VDEC_BASE + (0x0c*4), 0x04);
}

/* set Input selector & input pull-downs */
static void vadc_select_input(int vadc_in)
{
	switch (vadc_in) {
	case 0:
		reg32_write(AFE_INPFLT, 0x02);
		reg32_write(AFE_OFFDRV, 0x00);
		reg32_write(AFE_INPCONFIG, 0x1e);
		break;
	case 1:
		reg32_write(AFE_INPFLT, 0x02);
		reg32_write(AFE_OFFDRV, 0x00);
		reg32_write(AFE_INPCONFIG, 0x2d);
		break;
	case 2:
		reg32_write(AFE_INPFLT, 0x02);
		reg32_write(AFE_OFFDRV, 0x00);
		reg32_write(AFE_INPCONFIG, 0x4b);
		break;
	case 3:
		reg32_write(AFE_INPFLT, 0x02);
		reg32_write(AFE_OFFDRV, 0x00);
		reg32_write(AFE_INPCONFIG, 0x87);
		break;
	default:
		printf("error video input %d\n", vadc_in);
	}
}

/*!
 * Return attributes of current video standard.
 * Since this device autodetects the current standard, this function also
 * sets the values that need to be changed if the standard changes.
 * There is no set std equivalent function.
 *
 *  @return		None.
 */
void vadc_get_std(struct sensor_data *vadc)
{
	int tmp;
	int idx;

	/* Read PAL mode detected result */
	tmp = reg32_read(VDEC_VIDMOD);
	tmp &= (VDEC_VIDMOD_PAL_MASK | VDEC_VIDMOD_M625_MASK);

	if (tmp)
		idx = STD_PAL;
	else
		idx = STD_NTSC;

	vadc->std_id = idx;
	vadc->pixel_fmt = FMT_YUV444;
	vadc->width = video_fmts[idx].active_width;
	vadc->height = video_fmts[idx].active_height;
}

void vadc_config(u32 vadc_in)
{
	struct sensor_data vadc;

	/* map vafe,vdec,gpr,gpc address  */
	vafe_regbase = (u32 *)VADC_BASE_ADDR;
	vdec_regbase = (u32 *)VDEC_BASE_ADDR;

	vadc_power_up();

	/* clock config for vadc */
	reg32_write(VDEC_BASE + 0x320, 0xe3);
	reg32_write(VDEC_BASE + 0x324, 0x38);
	reg32_write(VDEC_BASE + 0x328, 0x8e);
	reg32_write(VDEC_BASE + 0x32c, 0x23);
	mxs_set_vadcclk();

	afe_init();

	/* select Video Input 0-3 */
	vadc_select_input(vadc_in);

	afe_voltage_clampingmode();

	vdec_init(&vadc);

	/*
	* current control loop will move sinewave input off below
	* the bottom of the signal range visible when the testbus is viewed as magnitude,
	* so have to break before this point while capturing ENOB data:
	*/
	afe_alwayson_clampingmode();
}