/* 2012-09-16: File added and changed by Sony Corporation */
/*
 * icx_lcd_tovis_R61505W.c
 *
 * Copyright 2011 Sony Corporation.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <common.h>

#ifdef CONFIG_ICX

#include <asm/errno.h>

#include "reg.h"
#include "icx_io.h"
#include "icx_smu.h"
#include "icx_gpio.h"
#include "icx_spi.h"
#include "icx_pwc.h"
#include "icx_pwm.h"
#include "icx_lcdc.h"
#include "icx_lcd.h"


/*******************/
/*@ LCDC operation */
/*******************/

static int  lcd_tovis_r61505w_power_on(const lcdc_config_t* config);
static void lcd_tovis_r61505w_power_off(void);

const lcd_ops_t lcd_tovis_r61505w_ops = {
	.power_on  = lcd_tovis_r61505w_power_on,
	.power_off = lcd_tovis_r61505w_power_off,
};

/*****************/
/*@ LCDC setting */
/*****************/

/* hsync */
#define LCD_R61505W_HAREA		LCDC_WIDTH_QVGA
#define	LCD_R61505W_HPULSE		16
#define	LCD_R61505W_HFRONTP		16
#define	LCD_R61505W_HBACKP		20
#define	LCD_R61505W_HBLANK		(LCD_R61505W_HFRONTP + LCD_R61505W_HPULSE + LCD_R61505W_HBACKP)
#define	LCD_R61505W_HTOTAL		(LCD_R61505W_HAREA   + LCD_R61505W_HBLANK)
#define	LCD_R61505W_HEDGE1		(LCD_R61505W_HFRONTP)
#define	LCD_R61505W_HEDGE2		(LCD_R61505W_HFRONTP + LCD_R61505W_HPULSE)

/* vsync */
#define LCD_R61505W_VAREA		LCDC_HEIGHT_QVGA
#define	LCD_R61505W_VPULSE		2
#define	LCD_R61505W_VFRONTP		4
#define	LCD_R61505W_VBACKP		2
#define	LCD_R61505W_VBLANK		(LCD_R61505W_VFRONTP + LCD_R61505W_VPULSE + LCD_R61505W_VBACKP)
#define	LCD_R61505W_VTOTAL		(LCD_R61505W_VAREA   + LCD_R61505W_VBLANK)
#define	LCD_R61505W_VEDGE1		(LCD_R61505W_VFRONTP)
#define	LCD_R61505W_VEDGE2		(LCD_R61505W_VFRONTP + LCD_R61505W_VPULSE)

/* default brightness */
#define	LCD_R61505W_PWM_DEFAULT	(PWM_TOTAL_100K / 2)

/* LCDC config */
const lcdc_config_t lcdc_tovis_r61505w_config = {
	.width	  = LCDC_WIDTH_QVGA,
	.height   = LCDC_HEIGHT_QVGA,

	.clkdiv   = SMU_PLLSEL_PLL3 | SMU_DIV(40),

	.frameadr = LCDC_FRAMEADR_DEFAULT,
	.areaadr  = LCDC_AREAADR_DEFAULT,
	.pol	  = (LCDC_CLKPOL_POS | LCDC_HPOL_POS | LCDC_VPOL_POS | LCDC_ENPOL_LOW),
	.htotal   = LCD_R61505W_HTOTAL, 
	.harea	  = LCD_R61505W_HAREA,
	.hedge1   = LCD_R61505W_HEDGE1, 
	.hedge2   = LCD_R61505W_HEDGE2,
	.vtotal   = LCD_R61505W_VTOTAL,
	.varea    = LCD_R61505W_VAREA,
	.vedge1   = LCD_R61505W_VEDGE1,
	.vedge2   = LCD_R61505W_VEDGE2,

	.backlight = {
		.dev  = PWM_DEV_1,
		.pol  = LCDC_BLPOL_NEG,
		.full = LCD_R61505W_PWM_DEFAULT,
		.div  = SMU_PLLSEL_PLL3 | SMU_DIV(4),
	},
};

/****************/
/*@ SPI setting */
/****************/

/* SPI config */
static spi_config_t spi_config_w = {
#ifdef CONFIG_BROWNIE
	.dev = SPI_DEV_SP1,
#else
	.dev = SPI_DEV_SP3,
#endif
	.clk = SPI_CLK_6MHZ,
	.cs  = SPI_CS_SEL_CS0,
	.pol = (SPI_CK_DLY_OFF | SPI_CK_POL_NEG | SPI_CS_POL_NEG),
	.nbw = SPI_NB_24BIT,
	.nbr = 0,
};

/* SPI start byte */
#define LCD_R61505W_SB_CMD	0x00700000	/* Set index register				*/
#define LCD_R61505W_SB_DATA	0x00720000	/* Writes instruction or RAM data	*/

#define LCD_R61505W_IR_SYNC	0xFFFE		/* transfer synchronize(local rule)	*/
#define LCD_R61505W_IR_WAIT	0xFFFF		/* wait command(local rule)			*/

#define	LCD_R61505W_HEART_BEAT_MAGIC_NUMBER	0xAA55

/* command type */
typedef struct lcd_r61505w_command {
	unsigned short  ir;
	unsigned short  data;
} lcd_r61505w_command_t;

/* SPI command */
static const lcd_r61505w_command_t spi_cmd_power_on[] = {
	{LCD_R61505W_IR_SYNC,   0}, /* Transfer Synchronize			*/
	{LCD_R61505W_IR_SYNC,   0},
	{LCD_R61505W_IR_SYNC,   0},
	{LCD_R61505W_IR_SYNC,   0},
	{0x00A4, 0x0001},			/* CALB on						*/
	{LCD_R61505W_IR_WAIT,   1},
	{0x0008, 0x0404},			/* FP=4,BP=4					*/
	{0x0030, 0x0907},			/* Gamma setting				*/
	{0x0031, 0xCB11},
	{0x0032, 0x0B08},
	{0x0033, 0x000D},
	{0x0034, 0x1201},
	{0x0035, 0x0800},
	{0x0036, 0x5405},
	{0x0037, 0x0A05},
	{0x0038, 0x0306},
	{0x0039, 0x1010},
	{0x0050, 0x0000},			/* Window address control		*/
	{0x0051, 0x00EF},
	{0x0052, 0x0000},
	{0x0053, 0x013F},
	{0x0007, 0x0100},			/* Power supply user setting	*/
	{0x0010, 0x0230},
	{0x0011, 0x0237},
	{0x0012, 0x01BC},
	{0x0013, 0x1700},
	{0x0029, 0x0058},
	{0x0060, 0x2700},			/* User setting					*/
	{0x0061, 0x0000},
	{0x006A, 0x0000},
	{0x0080, 0x0000},
	{0x0081, 0x0000},
	{0x0082, 0x0000},
	{0x0095, 0x0112},
	{0x0096, 0x0005},
	{0x0097, 0x0200},
	{0x0098, 0x0701},
	{0x0099, 0x0001},
	{0x009C, 0x0022},
	{0x0001, 0x0100},			/* Display other mode setting	*/
	{0x0002, 0x0200},
	{0x0003, 0x1030},
	{0x0009, 0x0001},
	{0x000A, 0x0000},
	{0x000C, 0x0110},
	{0x000D, 0x0000},
	{0x000E, 0x0030},
	{0x000F, 0x0001},
	{0x0020, 0x0000},
	{0x0021, 0x0000},
	{LCD_R61505W_IR_WAIT, 100},
	{0x00A1, LCD_R61505W_HEART_BEAT_MAGIC_NUMBER},
	{0x0007, 0x0100},			/* Display On Sequence			*/
	{0x0022, 0x0000},
};
#define	SPI_CMD_POWER_ON_NUM	(sizeof(spi_cmd_power_on)/sizeof(spi_cmd_power_on[0]))

static const lcd_r61505w_command_t spi_cmd_power_off[] = {
	{0x0007, 0x0000},			/* Display Off Sequence			*/
	{0x0012, 0x018C},			/* Power Supply Off Sequence	*/
};
#define	SPI_CMD_POWER_OFF_NUM	(sizeof(spi_cmd_power_off)/sizeof(spi_cmd_power_off[0]))

/******************/
/*@ Local routine */
/******************/

/*-----------------------------------------------------------------------------
 * Function : lcd_r61505w_send_command
 * Abstruct : Send a command & a data to LCD via SPI
 * Input    : none
 * Return   : <= 0(NG) / others(sent packet=success)
 *----------------------------------------------------------------------------*/
static int lcd_r61505w_send_command(const lcd_r61505w_command_t* func)
{
	u_int buf;
	int   ret = 0;

	if (func->ir == LCD_R61505W_IR_WAIT) {
		/* wait */
		udelay((unsigned long)func->data*1000);
		ret = func->data;
	}
	else if (func->ir == LCD_R61505W_IR_SYNC) {
		buf = LCD_R61505W_SB_CMD | func->data;
		ret = spi_write(&spi_config_w, &buf, 1);
	}
	else {
		/* send a command */
		buf = LCD_R61505W_SB_CMD | func->ir;
		ret = spi_write(&spi_config_w, &buf, 1);

		/* send a data */
		if (ret > 0) {
			buf = LCD_R61505W_SB_DATA | func->data;
			ret = spi_write(&spi_config_w, &buf, 1);
		}
	}

	return ret;
}


/*******************/
/*@ LCDC operation */
/*******************/

/*-----------------------------------------------------------------------------
 * Function : lcd_tovis_r61505w_power_on
 * Abstruct : Power on the LCD 
 * Input    : config : pointer to LCDC configure[OUT]
 * Return   : 0(success) / others(NG)
 *----------------------------------------------------------------------------*/
static int lcd_tovis_r61505w_power_on(const lcdc_config_t* config)
{
	int i;
	int ret;

	/* device reset */
	gpio_set_value(GPIO_LCD_XRST, 0x0);
	udelay(2000);
	gpio_set_value(GPIO_LCD_XRST, 0x1);

	udelay(2000);

	/* LCDC power on(supply clock) */
	lcdc_hw_init();

	/* LCDC initialize */
	lcdc_hw_set(config);

	/* start clock */
	lcdc_hw_bussel(LCDC_BUSSEL_BLACK);
	lcdc_hw_start();
	udelay(10000);

	/* H/W power on */
	spi_probe(&spi_config_w);
	for (i = 0; i < SPI_CMD_POWER_ON_NUM; i++) {
		ret = lcd_r61505w_send_command(&spi_cmd_power_on[i]);
		if (ret <= 0) {
			printf("R61505W: spi_write error(%d)\n", ret);
			return ret;
		}
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function : lcd_tovis_r61505w_power_off
 * Abstruct : Power off the LCD 
 * Input    : none
 * Return   : none
 *----------------------------------------------------------------------------*/
static void lcd_tovis_r61505w_power_off(void)
{
	int i;

	/* write black data */
	lcdc_hw_bussel(LCDC_BUSSEL_BLACK);

	udelay(40000);

	/* H/W power off */
	spi_probe(&spi_config_w);
	for (i = 0; i < SPI_CMD_POWER_OFF_NUM; i++) {
		lcd_r61505w_send_command(&spi_cmd_power_off[i]);
	}

	udelay(85000);

	/* reset LCDC,IMC */
	lcdc_hw_stop();
	lcdc_hw_exit();

	/* reset the device */
	gpio_set_value(GPIO_LCD_XRST, 0x0);
}

#endif /* CONFIG_ICX */
