/*******************************************************************************
**+--------------------------------------------------------------------------+**
**|                            ****                                          |**
**|                            ****                                          |**
**|                            ******o***                                    |**
**|                      ********_///_****                                   |**
**|                      ***** /_//_/ ****                                   |**
**|                       ** ** (__/ ****                                    |**
**|                           *********                                      |**
**|                            ****                                          |**
**|                            ***                                           |**
**|                                                                          |**
**|         Copyright (c) 1998-2003 Texas Instruments Incorporated           |**
**|                        ALL RIGHTS RESERVED                               |**
**|                                                                          |**
**| Permission is hereby granted to licensees of Texas Instruments           |**
**| Incorporated (TI) products to use this computer program for the sole     |**
**| purpose of implementing a licensee product based on TI products.         |**
**| No other rights to reproduce, use, or disseminate this computer          |**
**| program, whether in part or in whole, are granted.                       |**
**|                                                                          |**
**| TI makes no representation or warranties with respect to the             |**
**| performance of this computer program, and specifically disclaims         |**
**| any responsibility for any damages, special or consequential,            |**
**| connected with the use of this program.                                  |**
**|                                                                          |**
**+--------------------------------------------------------------------------+**
*******************************************************************************/

/**************************************************************************
 * FILE PURPOSE :   HAL code for LCD LIDD controller.
 **************************************************************************
 * FILE NAME    :   lidd_hal.c
 *
 * DESCRIPTION  :
 *  HAL code for LCD LIDD controller.
 *
 *  CALL-INs:
 *
 *  CALL-OUTs:
 *
 *  User-Configurable Items:
 *
 *  (C) Copyright 2003, Texas Instruments, Inc.
 *************************************************************************/
typedef void TI_HD47780_T;




typedef struct 
{
    volatile unsigned int config;
    volatile unsigned int addr;
    volatile unsigned int data;
} TI_LIDD_REGS_T;

typedef struct 
{
    volatile unsigned int   revision;
    volatile unsigned int   cntl;
    volatile unsigned int   status;
    volatile unsigned int   lidd_cntl;
    volatile TI_LIDD_REGS_T cs[2];
    volatile unsigned int   raster_cntl;
    volatile unsigned int   raster_timing_0;
    volatile unsigned int   raster_timing_1;
    volatile unsigned int   raster_timing_2;
    volatile unsigned int   subpanel_disp;
    volatile unsigned int   reserved;
    volatile unsigned int   dma_cntl;
    volatile unsigned int   frame0_base_addr;
    volatile unsigned int   frame0_ceiling;
    volatile unsigned int   frame1_base_addr;
    volatile unsigned int   frame1_ceiling;

} TI_LCD_CNTL_REGS_T; 

typedef struct
{
    unsigned char     num_lcd_inst;
    TI_HD47780_T*      lcd_inst[2];
    TI_LCD_CNTL_REGS_T*       regs;
    unsigned int       active_inst;
    unsigned int          disp_row;
    unsigned int          disp_col;
    unsigned int         line_wrap;
    unsigned int      cursor_state;
    unsigned int      cursor_shift;
    unsigned int               row;
    unsigned int               col;

} LIDD_HAL_OBJ_T;


#include "hd44780_hal.h"
#include "haltypes.h"
#include "lidd_hal.h"



static int ti_lidd_p_set_to_xy(LIDD_HAL_OBJ_T*, int, int);
static int ti_lidd_p_update_properties(LIDD_HAL_OBJ_T*);
static int ti_lidd_p_lwrap_cursor_move(LIDD_HAL_OBJ_T *p, unsigned int direction);
static int ti_lidd_p_wr_or_rd(LIDD_HAL_OBJ_T *p, unsigned int cmd, unsigned int data);



/*-----------------------------------------------------------------------------
 * Updates the properties of the LCD. 
 *
 * Returns 0 on success or -1 on failure.
 *---------------------------------------------------------------------------*/
int ti_lidd_p_update_properties(LIDD_HAL_OBJ_T *p)
{
    unsigned int passive_inst = !p->active_inst;

    if(p->num_lcd_inst > 1)
/*      ti_hd47780_ioctl(p->lcd_inst + passive_inst, TI_LIDD_CURSOR_STATE, 0); */
        ti_hd47780_ioctl(p->lcd_inst[passive_inst], TI_LIDD_CURSOR_STATE , p->cursor_state);

/*  ti_hd47780_ioctl(p->lcd_inst + p->active_inst, TI_LIDD_CURSOR_STATE , p->cursor_state); */
    ti_hd47780_ioctl(p->lcd_inst[p->active_inst], TI_LIDD_CURSOR_STATE , p->cursor_state);

    return(0);
}

/*---------------------------------------------------------
 * Sets the cursor at the specified location.
 * x is the column, y is the row. 
 *
 * Returns 0 on success otherwise -1.
 *-------------------------------------------------------*/
int ti_lidd_p_set_to_xy(LIDD_HAL_OBJ_T *p, int x, int y)
{
    int update = 0;

    if(p->line_wrap)
    {
	    /* wrap the row and col if required. */
        p->col  = x = x % p->disp_col;
	    p->row  = y = y % p->disp_row; 
    }

    /* Figure out if this causes a moving to lcd 0 to lcd 1. */
    if((p->num_lcd_inst == 2) && (y > p->disp_row/2 - 1))
    {
        y = y%2;

	if(p->active_inst == 0)
	    update = 1;

        p->active_inst = 1;
    }
    else
    {
        /* Figure out if this causes a moving to lcd 1 to lcd 0. */
        if(p->active_inst == 1)
	    update = 1;

        p->active_inst = 0;
    }
       
    if(update) ti_lidd_p_update_properties(p);

    ti_hd47780_ioctl(p->lcd_inst[p->active_inst], TI_LIDD_GOTO_XY, x << 8 | y);

    return (0);
}

/*-----------------------------------------------------------------------------
 * Wraps a line i.e. when the last of column of the row is reached, the cursor
 * is moved to the next row, first col. When the last row and last col is reached 
 * the cursor is moved to the first row. 
 *
 * Returns: 0 if now wrap was done. 1, if a wrap was carried out.   
 *---------------------------------------------------------------------------*/
int ti_lidd_p_lwrap_cursor_move(LIDD_HAL_OBJ_T *p, unsigned int direction)
{   
    int ret_val = 0;

    if(direction) /* move right. */
    {
        p->col = (++p->col) % (p->disp_col);
        if(!p->col) 
        {
    	    /* line wrapped. */
    	    p->row = (++p->row) % p->disp_row;
            

    	    /* Indicate the lcd of the line wrapping. */
            ti_lidd_p_set_to_xy(p, p->col, p->row);

            

    	    ret_val = 1;
	    }
    }
    else     /* move left. */
    {
    	if(!p->col)
    	{
    	    /* line wrapped. */
    	    p->col = p->disp_col - 1;
    	    p->row = (!p->row) ? p->disp_row - 1 : --p->row;

    	    /* Indicate the lcd of the line wrapping. */
                ti_lidd_p_set_to_xy(p, p->col, p->row);

            
    	    ret_val = 1;
    	}
	    else
	    {
	        p->col--;
	    }
    }
    return(ret_val);
}

/*-----------------------------------------------------------------------------
 * Writes or reads a character to/from LCD.
 *
 * Returns 0 on success or -1 on failure. 
 *---------------------------------------------------------------------------*/
int ti_lidd_p_wr_or_rd(LIDD_HAL_OBJ_T *p, unsigned int cmd, unsigned int data)
{

    /* Write the character on the active hd47780 instance. */
    ti_hd47780_ioctl(p->lcd_inst[p->active_inst], cmd, data);

    /* Update the rows and columns for the next read or write, only if enabled
     * for line wrap. */
    if(p->line_wrap)
        ti_lidd_p_lwrap_cursor_move(p, p->cursor_shift);

    return (0);
}



/* LCD controller configuration */

#define LCD_LIDD_MCKL_FREQ                          6250 /* specify in KHz */

#define LCD_LIDD_P0_RS_SETUP_CYC     1
#define LCD_LIDD_P0_RS_HOLD_CYC      1
#define LCD_LIDD_P0_RS_DUR_CYC       4

#define LCD_LIDD_P1_RS_SETUP_CYC     1
#define LCD_LIDD_P1_RS_HOLD_CYC      1
#define LCD_LIDD_P1_RS_DUR_CYC       4

#define LCD_LIDD_P0_WS_SETUP_CYC     1
#define LCD_LIDD_P0_WS_HOLD_CYC      1
#define LCD_LIDD_P0_WS_DUR_CYC       4

#define LCD_LIDD_P1_WS_SETUP_CYC     1       
#define LCD_LIDD_P1_WS_HOLD_CYC      1
#define LCD_LIDD_P1_WS_DUR_CYC       4


#define LCD_CLK_DIVISOR(x)          ((x) << 8)
#define LCD_CLK_DIVISOR_MASK        LCD_CLK_DIVISOR(0xFF)
#define LCD_MODE_SEL_MASK           0x01

#define LCD_END_OF_FRAME1           (1 << 9)
#define LCD_END_OF_FRAME0           (1 << 8)
#define LCD_PALETTE_LOADED          (1 << 6)
#define LCD_FIFO_UNDERFLOW          (1 << 5)
#define LCD_AC_BIAS_COUNT_STATUS    (1 << 3)
#define LCD_SYNC_LOST               (1 << 2)
#define LCD_FRAME_DONE              (1 << 0)


#define LCD_CS1_E1_POL_INV          0x80
#define LCD_CS0_E0_POL_INV          0x40
#define LCD_WE_RW_POL_INV           0x20
#define LCD_OE_E_POL_INV            0x10
#define LCD_ALE_POL_INV             0x08
#define LCD_LIDD_MODE(x)            ((x) << 0)

#define LCD_HITACHI_MODE            0x04
#define LCD_MPU80_ASYNC_MODE        0x03
#define LCD_MPU80_SYNC_MODE         0x02
#define LCD_MPU68_ASYNC_MODE        0x01
#define LCD_MPU68_SYNC_MODE         0x00

#define LCD_LIDD_MODE_MASK          LCD_LIDD_MODE(0x07)

#define LCD_WS_SETUP(x)             ((x) << 27)
#define LCD_WS_WIDTH(x)             ((x) << 21)
#define LCD_WS_HOLD(x)              ((x) << 17)
#define LCD_RS_SETUP(x)             ((x) << 12)
#define LCD_RS_WIDTH(x)             ((x) << 6)
#define LCD_RS_HOLD(x)              ((x) << 2)
#define LCD_LIDD_DELAY(x)           ((x) << 0)

#define LCD_WS_SETUP_MASK           LCD_WS_SETUP(0x1F)
#define LCD_WS_WIDTH_MASK           LCD_WS_WIDTH(0x3F)
#define LCD_WS_HOLD_MASK            LCD_WS_HOLD(0x0F)
#define LCD_RS_SETUP_MASK           LCD_RS_SETUP(0x1F)
#define LCD_RS_WIDTH_MASK           LCD_RS_WIDTH(0x3F)
#define LCD_RS_HOLD_MASK            LCD_RS_HOLD(0x0F)

#define LCD_LIDD_DELAY_MASK         LCD_LIDD_DELAY(0x03)


#define LCD_PASSIVE_565_MODE        (1 << 24)
#define LCD_ACTIVE_ALT_MAP_MODE     (1 << 23)
#define LCD_NIBBLE_MODE             (1 << 22)
#define LCD_PALETTE_LOAD_MODE(x)    ((x) << 20)
#define PALETTE_AND_DATA            0x00
#define PALETTE_ONLY                0x01
#define DATA_ONLY                   0x02
#define LCD_FIFO_DMA_DELAY_REQ(x)   ((x) << 12)
#define LCD_MONO_8BIT_MODE          (1 << 9)
#define LCD_RASTER_DATA_ORDER       (1 << 8)
#define LCD_TFT_MODE                (1 << 7)
#define LCD_UNDERFLOW_INT_ENA       (1 << 6)
#define LCD_SYNC_LOST_INT_ENA       (1 << 5)
#define LCD_LOAD_DONE_INT_ENA       (1 << 4)
#define LCD_FRAME_DONE_INT_ENA      (1 << 3)
#define LCD_AC_BIAS_INT_ENA         (1 << 2)
#define LCD_MONOCHROME_MODE         (1 << 1)
#define LCD_RASTER_ENABLE           (1 << 0)


#define LCD_HSYNC_BACK_PORCH(x)     ((x) << 24)
#define LCD_HSYNC_FRONT_PORCH(x)    ((x) << 16)
#define LCD_HSYNC_PULSE_WIDTH(x)    ((x) << 10)
#define LCD_PIXELS_PER_LINE(x)      ((((x) / 16) - 1) << 4)


#define LCD_VSYNC_BACK_PORCH(x)     ((x) << 24)
#define LCD_VSYNC_FRONT_PORCH(x)    ((x) << 16)
#define LCD_VSYNC_PULSE_WIDTH(x)    ((x) << 10)
#define LCD_LINES_PER_PANEL(x)      (((x) - 1) << 0)


#define LCD_SYNC_PIXEL_CLK_CTRL     (1 << 25)
#define LCD_SYNC_FALLING_EDGE       (1 << 24)
#define LCD_INVERT_OE               (1 << 23)
#define LCD_INVERT_PIXEL_CLK        (1 << 22)
#define LCD_INVERT_HSYNC            (1 << 21)
#define LCD_INVERT_VSYNC            (1 << 20)
#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x)      ((x) << 16)
#define LCD_AC_BIAS_FREQUENCY(x)    ((x) << 8)


#define LCD_SUBPANEL_ENABLE         (1 << 31)
#define LCD_SUBPANEL_HIGH_OR_LOW    (1 << 29)
#define LCD_SUBPANEL_THRESHOLD(x)    ((x) << 16)
#define LCD_DEFAULT_PIXEL_DATA(x)   ((x) << 4)


#define LCD_DMA_BURST_SIZE(x)       ((x) << 4)
#define LCD_DMA_BURST_1             0x0
#define LCD_DMA_BURST_2             0x1
#define LCD_DMA_BURST_4             0x2
#define LCD_DMA_BURST_8             0x3
#define LCD_DMA_BURST_16            0x4
#define LCD_END_OF_FRAME_INT_ENA    (1 << 2)
#define LCD_DMA_BIG_ENDIAN          (1 << 1)
#define LCD_DUAL_FRAME_BUFFER_ENABLE  (1 << 0)


LIDD_HAL_OBJ_T* ti_lidd_hal_init(TI_LIDD_INFO_T* p_lidd_info)
{
    static int g_lcd_inst = 0;
    LIDD_HAL_OBJ_T *p = NULL;        
    TI_LCD_CNTL_REGS_T *p_regs = NULL;
    
    int i;

    /* Initialize the controller, if not done already. */
    p = os_platform_malloc(sizeof(LIDD_HAL_OBJ_T));
    if(!p) return (NULL);
    
    os_platform_memset(p, 0, sizeof(LIDD_HAL_OBJ_T));

    p->regs = (TI_LCD_CNTL_REGS_T*) p_lidd_info->base_addr;

    if(g_lcd_inst == 2) return (NULL);

    if(g_lcd_inst == 0)
    {
        p_regs              = p->regs;

	    p_regs->cntl       &= ~1;                  /* Sets it in the HD47780 mode. */
        p_regs->cntl       &= ~(LCD_CLK_DIVISOR_MASK);
        p_regs->cntl       |=  LCD_CLK_DIVISOR(10); /* modify the code here for 
					      instance where mclk is required. */
        p_regs->cs[0].config |=  LCD_RS_SETUP(LCD_LIDD_P0_RS_SETUP_CYC) 
		               | LCD_RS_WIDTH(LCD_LIDD_P0_RS_DUR_CYC) 
		               | LCD_RS_HOLD(LCD_LIDD_P0_RS_HOLD_CYC);
        p_regs->cs[1].config |=  LCD_RS_SETUP(LCD_LIDD_P1_RS_SETUP_CYC) 
		               | LCD_RS_WIDTH(LCD_LIDD_P1_RS_DUR_CYC) 
			       | LCD_RS_HOLD(LCD_LIDD_P1_RS_HOLD_CYC);
        p_regs->cs[0].config |=  LCD_WS_SETUP(LCD_LIDD_P0_WS_SETUP_CYC) 
		               | LCD_WS_WIDTH(LCD_LIDD_P0_WS_DUR_CYC) 
			       | LCD_WS_HOLD(LCD_LIDD_P0_WS_HOLD_CYC);
        p_regs->cs[1].config |=  LCD_WS_SETUP(LCD_LIDD_P1_WS_SETUP_CYC) 
		               | LCD_WS_WIDTH(LCD_LIDD_P1_WS_DUR_CYC) 
			       | LCD_WS_HOLD(LCD_LIDD_P1_WS_HOLD_CYC);

    }

    p->active_inst  = 0;
    p->num_lcd_inst = p_lidd_info->num_lcd;
    p->disp_row     = p_lidd_info->disp_row;
    p->disp_col     = p_lidd_info->disp_col;
    p->cursor_state = p_lidd_info->cursor_show;
    p->line_wrap    = p_lidd_info->line_wrap;
    p->row          = 0;
    p->col          = 0;
    p->cursor_shift = RIGHT;
   
    if(p_lidd_info->lcd_type == 4)
    {
	p_regs->lidd_cntl &= 0xfffffff8;
	p_regs->lidd_cntl |= 0x4;

    for(i = 0; i < p->num_lcd_inst; i++)
	{
            if(!(p->lcd_inst[i] = ti_hd47780_init((unsigned int*)(&p_regs->cs[i + g_lcd_inst].addr), 
			                                      (unsigned int*) &p_regs->cs[i + g_lcd_inst].data, 
		                                          (p->disp_row/p->num_lcd_inst), 
		                                          (p->disp_col/p->num_lcd_inst),
		                                           p_lidd_info->cpu_freq)))
	        goto hd47780_init_error;

	    ti_hd47780_ioctl(p->lcd_inst[i], TI_LIDD_CURSOR_SHIFT, p->cursor_shift);
        ti_hd47780_ioctl(p->lcd_inst[i], TI_LIDD_DISPLAY, ON);
	    ti_hd47780_ioctl(p->lcd_inst[i], TI_LIDD_CURSOR_STATE,  (p->cursor_state && i==0) ? ON : OFF);
	    ti_hd47780_ioctl(p->lcd_inst[i], TI_LIDD_DISPLAY_MOVE, OFF);
	    ti_hd47780_ioctl(p->lcd_inst[i], TI_LIDD_BLINK, (p_lidd_info->cursor_blink && i==0) ? ON: OFF);
	    ti_hd47780_ioctl(p->lcd_inst[i], TI_LIDD_CLEAR_SCREEN, 0);


        }
    }

    g_lcd_inst += p->num_lcd_inst;

    return (p);

hd47780_init_error: 
    while(i)
    {
        ti_hd47780_cleanup(p->lcd_inst + i);
	p->num_lcd_inst--;
    }

    os_platform_free(p);

    return (NULL);

}

int ti_lidd_hal_ioctl(LIDD_HAL_OBJ_T *p, unsigned int cmd, unsigned int val)
{
    switch(cmd)
    {
        case TI_LIDD_CLEAR_SCREEN:
	    ti_hd47780_ioctl(p->lcd_inst[0], cmd, val);
	    if(p->num_lcd_inst > 1)
	        ti_hd47780_ioctl(p->lcd_inst[1], cmd, val);
	    p->row  = 0;
	    p->col  = 0;
	    break;

	case TI_LIDD_LINE_WRAP:
	    p->line_wrap = val ? 1 : 0;
	    if(p->line_wrap)
            { 
	        p->active_inst         = 0;
	        ti_lidd_p_update_properties(p);

		/* disable display shift */
	        if(p->num_lcd_inst > 1)
	            ti_hd47780_ioctl(p->lcd_inst[1], TI_LIDD_DISPLAY_MOVE, val);
	        ti_hd47780_ioctl(p->lcd_inst[0], TI_LIDD_DISPLAY_MOVE, val);
	    }
	    break;

	case TI_LIDD_CURSOR_HOME:
	    if(p->num_lcd_inst > 1)
	    {
		    ti_hd47780_ioctl(p->lcd_inst[1], cmd, 0);
	        ti_hd47780_ioctl(p->lcd_inst[1], TI_LIDD_CURSOR_STATE, 0);
	    }
	    ti_hd47780_ioctl(p->lcd_inst[0], cmd, 0);
	    p->row  = 0;
	    p->col  = 0;
	    break;

	case TI_LIDD_DISPLAY:
	    if(p->num_lcd_inst > 1)
	        ti_hd47780_ioctl(p->lcd_inst[1], cmd, val);
	    ti_hd47780_ioctl(p->lcd_inst[0], cmd, val);
	    break;

	case TI_LIDD_GOTO_XY:
            ti_lidd_p_set_to_xy(p, (val & 0xff00) >> 8 , val & 0xff);
	    break;

	case TI_LIDD_BLINK:
	    if(p->num_lcd_inst > 1)
	        ti_hd47780_ioctl(p->lcd_inst[1], cmd, val);
	    ti_hd47780_ioctl(p->lcd_inst[0], cmd, val);
	    break;

	case TI_LIDD_CURSOR_STATE:
	    ti_hd47780_ioctl(p->lcd_inst[p->active_inst], cmd, val);
	    p->cursor_state = val ? 1 : 0;
	    break;

	case TI_LIDD_CURSOR_SHIFT:
	    if(p->num_lcd_inst > 1)
	        ti_hd47780_ioctl(p->lcd_inst[1], cmd, val);
	    ti_hd47780_ioctl(p->lcd_inst[0], cmd, val);
	    p->cursor_shift = (val) ? 1:0;
	    break;

	case TI_LIDD_CURSOR_MOVE:
	    {
	        int action = 1;

	        if(p->line_wrap && ti_lidd_p_lwrap_cursor_move(p, val))
	            action = 0;

	        if(action)
	            ti_hd47780_ioctl(p->lcd_inst[p->active_inst], cmd, val);
	    }
	    break;

	case TI_LIDD_DISPLAY_SHIFT:
	    if(p->num_lcd_inst > 1)
	        ti_hd47780_ioctl(p->lcd_inst[1], cmd, val);
	    ti_hd47780_ioctl(p->lcd_inst[0], cmd, val);
	    p->line_wrap = 0; /* p->row = 0; p->col = 0; */
	    break;

	case TI_LIDD_DISPLAY_MOVE:
	    ti_hd47780_ioctl(p->lcd_inst[p->active_inst], cmd, val);
	    break;

	case TI_LIDD_WR_CHAR:
	case TI_LIDD_RD_CHAR:
            ti_lidd_p_wr_or_rd(p, cmd, val);
	    break;

	case TI_LIDD_RD_CMD:
	    ti_hd47780_ioctl(p->lcd_inst[p->active_inst], cmd, val);
	    break;
	
	    
	default:
	    break;
        
    }

    return(0);
}

int ti_lidd_hal_open(LIDD_HAL_OBJ_T *p)
{
    return (0);
}

int ti_lidd_hal_close(LIDD_HAL_OBJ_T *p)
{
    return (0);
}

int ti_lidd_hal_write(LIDD_HAL_OBJ_T *p, char *data, unsigned int len)
{
    int i = 0;
    for(i= 0; i< len; i++)
    {
        if(ti_lidd_p_wr_or_rd(p, TI_LIDD_WR_CHAR, data[i]))
	    break;
       /* taskDelay(1); */
    }
    return(i);
}

int ti_lidd_hal_read(LIDD_HAL_OBJ_T *p, char *data, unsigned int len)
{
    int i = 0;
    for(i= 0; i< len; i++)
        if(ti_lidd_p_wr_or_rd(p, TI_LIDD_RD_CHAR, (unsigned int)&(data[i])))
	    break;
    return(i);
}

int ti_lidd_hal_cleanup(LIDD_HAL_OBJ_T *p)
{
    while(p->num_lcd_inst--)
        ti_hd47780_cleanup(p->lcd_inst + p->num_lcd_inst);

    os_platform_free(p);

    return(0);
}
