/* 2009-08-18: File added by Sony Corporation */
/*
 *  LT048MDJ81 LCD Support for PGPF
 *
 *  Copyright 2009 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;  version 2 of the  License.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>

#include <asm/arch/hardware.h>
#include <asm/arch/smu.h>

#include "lcdc.h"
#include "mp200_lcdhw.h"
#include <asm/arch/pgpf/lcdhw.h>

#define LCD_LT048MDJ81_HPULSE      8
#define LCD_LT048MDJ81_HFRONTP     24
#define LCD_LT048MDJ81_HBACKP      8
#define LCD_LT048MDJ81_VPULSE      10
#define LCD_LT048MDJ81_VFRONTP     4
#define LCD_LT048MDJ81_VBACKP      2
#define LCD_LT048MDJ81_HAREA_VAL   480
#define LCD_LT048MDJ81_VAREA_VAL   272

#if defined(CONFIG_PLL3_238MHZ)
#define LCD_LCLK_DIV 28
#define LCD_LT048MDJ81_DIVLCDLCLK (SMU_PLLSEL_PLL3 | SMU_DIV28)
#elif defined(CONFIG_PLL3_230MHZ)
#define LCD_LCLK_DIV 26
#define LCD_LT048MDJ81_DIVLCDLCLK (SMU_PLLSEL_PLL3 | SMU_DIV26)
#endif /* CONFIG_PLL3_* */
#define LCD_LT048MDJ81_PIXCLOCK (1.0 / (PGPF_PLL3_CLK / LCD_LCLK_DIV) * 1000000 * 1000000)

#define LCD_LT048MDJ81_HBLANK_VAL (LCD_LT048MDJ81_HFRONTP + LCD_LT048MDJ81_HPULSE + LCD_LT048MDJ81_HBACKP)
#define LCD_LT048MDJ81_VBLANK_VAL (LCD_LT048MDJ81_VFRONTP + LCD_LT048MDJ81_VPULSE + LCD_LT048MDJ81_VBACKP)

#define LCD_LT048MDJ81_CONTROL_BASE (LCD_CLKPOL_POS | LCD_HPOL_POS | LCD_VPOL_POS | LCD_ENPOL_HI)
#if defined(CONFIG_MP200_LCD_RGB666)
#define LCD_LT048MDJ81_CONTROL (LCD_OFORMAT_RGB666 | LCD_LT048MDJ81_CONTROL_BASE)
#else /* CONFIG_MP200_LCD_RGB666 */
#define LCD_LT048MDJ81_CONTROL (LCD_OFORMAT_RGB565 | LCD_LT048MDJ81_CONTROL_BASE)
#endif /* CONFIG_MP200_LCD_RGB666 */

#define LCD_LT048MDJ81_HTOTAL (LCD_LT048MDJ81_HAREA_VAL + LCD_LT048MDJ81_HBLANK_VAL)
#define LCD_LT048MDJ81_HAREA  (LCD_LT048MDJ81_HAREA_VAL)
#define LCD_LT048MDJ81_HEDGE1 (LCD_LT048MDJ81_HFRONTP)
#define LCD_LT048MDJ81_HEDGE2 (LCD_LT048MDJ81_HFRONTP + LCD_LT048MDJ81_HPULSE)
#define LCD_LT048MDJ81_VTOTAL (LCD_LT048MDJ81_VAREA_VAL + LCD_LT048MDJ81_VBLANK_VAL)
#define LCD_LT048MDJ81_VAREA  (LCD_LT048MDJ81_VAREA_VAL)
#define LCD_LT048MDJ81_VEDGE1 (LCD_LT048MDJ81_VFRONTP)
#define LCD_LT048MDJ81_VEDGE2 (LCD_LT048MDJ81_VFRONTP + LCD_LT048MDJ81_VPULSE)

#define _DEBUG_LCD  0x00

#define DEV_NAME "pgpf_lcd_lt048mdj81"

#define printk_err(fmt, arg...) \
	do{                     \
		printk(KERN_ERR DEV_NAME ": %s: " fmt, __func__, ## arg); \
	}while(0)

#define printk_wrn(fmt, arg...) \
	do{                     \
		printk(KERN_WARNING DEV_NAME ": %s: " fmt, __func__, ## arg); \
	}while(0)

#define printk_info(fmt, arg...) \
	do{                      \
		printk(KERN_INFO DEV_NAME ": " fmt, ## arg); \
	}while(0)

#if _DEBUG_LCD
#define printk_dbg(level, fmt, arg...) \
	do{                            \
		if(level > 0) printk(KERN_DEBUG DEV_NAME ": %s: " fmt, __func__, ## arg); \
	}while(0)
#else
#define printk_dbg(level, fmt, arg...) \
	;
#endif

static int pgpf_lcdhw_lt048mdj81_resume(int restore, struct pgpf_bl_device* bl_dev)
{
	/* set to pwm */
	if (bl_dev && bl_dev->prep) {
		bl_dev->prep();
	}

	/* LCD_PWRON on */
	pgpf_set_lcd_pwron(1);

	/* wait for VDD on */
	mdelay(10);

	if (restore) {
		lcd_hw_imc_hw_init();
		lcd_controller_hw_init();
		pgpf_lcdhw_controller_hw_init();
	}

	pgpf_lcdhw_change_gio_settings(1);

	/* LCD start (PXCLK, DATA, ...) */
	lcd_hw_start();

	/* wait 1 ms after LCD out */
	mdelay(1);

	/* LCD_PON on */
	pgpf_set_lcd_pon(1);

	/* wait over 6 frames */
	mdelay(110);

	/* Backlight on */
	if (bl_dev && bl_dev->resume) {
		bl_dev->resume();
	}

	return 0;
}

static int pgpf_lcdhw_lt048mdj81_suspend(int save, struct pgpf_bl_device* bl_dev)
{
	/* LCD_PON off */
	pgpf_set_lcd_pon(0);

	/* wait 1 frame (max) */

	/* Backlight off */
	if (bl_dev && bl_dev->suspend) {
		bl_dev->suspend();
	}

	/* set to gpio */
	if (bl_dev && bl_dev->post) {
		bl_dev->post();
	}

	/* wait over 4 frames after LCD_PON off */
	mdelay(75);

	/* LCD stop (PXCLK, DATA, ...) */
	lcd_hw_stop();

	pgpf_lcdhw_change_gio_settings(0);

	/* wait 1ms after LCD stop*/
	mdelay(1);

	/* LCD_PWRON off */
	pgpf_set_lcd_pwron(0);

	return 0;
}


static int pgpf_lcdhw_lt048mdj81_init(struct pgpf_bl_device* bl_dev)
{
	if (!(lcd_hw_chk_status() & LCD_LCDOUT_ENABLE)) {
		/* LCD Module initialize */
		pgpf_lcdhw_change_gio_settings(0);
		pgpf_lcdhw_setup_clock();
		pgpf_lcdhw_lt048mdj81_resume(1, bl_dev);
	} else {
		lcd_hw_imc_hw_init();
		lcd_controller_hw_init();
		pgpf_lcdhw_controller_hw_init();
	}

	return 0;
}

static void pgpf_lcdhw_lt048mdj81_exit(struct pgpf_bl_device* bl_dev)
{
	pgpf_lcdhw_lt048mdj81_suspend(0, bl_dev);
}

static struct pgpf_lcdhw_device pgpf_lcdhw_lt048mdj81_dev = {
	.init = pgpf_lcdhw_lt048mdj81_init,
	.exit = pgpf_lcdhw_lt048mdj81_exit,
	.suspend = pgpf_lcdhw_lt048mdj81_suspend,
	.resume = pgpf_lcdhw_lt048mdj81_resume,

	.config.xres = LCD_LT048MDJ81_HAREA_VAL,
	.config.yres = LCD_LT048MDJ81_VAREA_VAL,

	.config.pixclock = LCD_LT048MDJ81_PIXCLOCK,
	.config.divlcdlclk = LCD_LT048MDJ81_DIVLCDLCLK,

	.config.control = LCD_LT048MDJ81_CONTROL,

	.config.htotal = LCD_LT048MDJ81_HTOTAL,
	.config.harea  = LCD_LT048MDJ81_HAREA,
	.config.hedge1 = LCD_LT048MDJ81_HEDGE1,
	.config.hedge2 = LCD_LT048MDJ81_HEDGE2,
	.config.vtotal = LCD_LT048MDJ81_VTOTAL,
	.config.varea  = LCD_LT048MDJ81_VAREA,
	.config.vedge1 = LCD_LT048MDJ81_VEDGE1,
	.config.vedge2 = LCD_LT048MDJ81_VEDGE2,
};

int pgpf_lcdhw_register(struct pgpf_lcdhw_device** lcd_dev, struct platform_device* pf_dev)
{
	printk_dbg(_DEBUG_LCD, "pgpf_lcdhw_register() called\n");
	*lcd_dev = &pgpf_lcdhw_lt048mdj81_dev;
	return 0;
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sony Corporation");
MODULE_DESCRIPTION("LT048MDJ81 LCD Support for PGPF");
