//==========================================================================
//
//      TouchPanel.c
//
//      pmic adc for touchpanel support on Freescale MXC platforms,
//      base on Freescale Linux BSP
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):
// Contributors:
// Date:
// Purpose:
// Description:
// Copyright:    2008 Kinpo Beijing, Inc.
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <redboot.h>
#include <stdlib.h>
#include <pkgconf/hal.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_soc.h>
#include <cyg/io/drawmenu.h>
#include <cyg/io/paint_lcd.h>
#include <cyg/hal/kinpo_g23.h>

//-----------------If Touch Panel Changes , modify Parameter here !-------------

cyg_int32 ScreenWidth=480;
cyg_int32 ScreenHeigh=272;
//#########################################  
//# EPIT1                                  #  
//# $53F9_4000 to $53F9_7FFF              #  
//#########################################  
#define EPIT1_BASE_ADDR 0x53F94000 
#define EPIT1_EPITCR (EPIT1_BASE_ADDR+0x00) //  32bit timer 2 control reg
#define EPIT1_EPITSR (EPIT1_BASE_ADDR+0x04) //  32bit timer 2 prescaler reg
#define EPIT1_EPITLR (EPIT1_BASE_ADDR+0x08) //  32bit timer 2 compare reg
#define EPIT1_EPITCMPR (EPIT1_BASE_ADDR+0x0C) //  32bit timer 2 capture reg
#define EPIT1_EPITCNR (EPIT1_BASE_ADDR+0x10) //  32bit timer 2 counter reg


//-------------------------------
#define TRUE 1
#define FALSE 0


//-------------------------------

typedef enum
{
    PMIC_SUCCESS = 0,     /*!< The requested operation was successfully completed. */
    PMIC_ERROR = -1,      /*!< The requested operation could not be completed
                               due to an error.                               */
    PMIC_PARAMETER_ERROR = -2,      /*!< The requested operation failed because
                                         one or more of the parameters was
                                         invalid.                             */
    PMIC_NOT_SUPPORTED = -3,        /*!< The requested operation could not be
                                         completed because the PMIC hardware
                                         does not support it. */
    PMIC_CLIENT_NBOVERFLOW = -4,    /*!< The requested operation could not be
                                         completed because there are too many 
                                         PMIC client requests */
    PMIC_MALLOC_ERROR = -5,         /*!< Error in malloc function             */
    PMIC_UNSUBSCRIBE_ERROR = -6,    /*!< Error in un-subscribe event          */
    PMIC_EVENT_NOT_SUBSCRIBED = -7, /*!< Event occur and not subscribed       */
    PMIC_EVENT_CALL_BACK = -8,      /*!< Error - bad call back                */  
} PMIC_STATUS;

//--------------------------------


#define REG_ADC_0                0x2B
#define REG_ADC_1                0x2C
#define REG_ADC_2                0x2D

//------------------- All functions --------------------
bool GetTouchPanelPoint(cyg_uint16 * X, cyg_uint16 *Y);
void SetMiliSecond(cyg_uint32 timer);


static bool mc13783_adc_read_ts(cyg_int8* outbuffer);                   
static bool pmic_adc_set_touch_mode(cyg_uint8 mode);
static PMIC_STATUS mc13783_adc_convert(void);
static void pmic_adc_deactive(void);
void pmic_set(cyg_uint32 addr, cyg_uint32 data, cyg_uint32 mask);
extern unsigned int pmic_reg(unsigned int reg, unsigned int val, unsigned int write);

//-----------------------------------------------------------------------------
//
// Function: GetTouchPanelPoint
//
// This function is to get x,y coordinate.
//
// Parameters:

//      timer
//          [in] 

// Returns:
//     True or false
//---------------------------------------------------------------------------------
bool GetTouchPanelPoint(cyg_uint16 * X, cyg_uint16 *Y)
{
        cyg_int8     g_test[32];	
        cyg_uint16   temp1 =0;
        cyg_uint16*  p_get_result = (cyg_uint16 *) g_test;
     	if(mc13783_adc_read_ts(g_test)==FALSE)
             return FALSE;
        temp1  = * p_get_result;
        p_get_result++;
        temp1 += * p_get_result;
        p_get_result++;
        temp1 += * p_get_result;
        temp1 = temp1/3;          // get average of x
        temp1 = (temp1*ScreenWidth)/1024 ;
        *X    = temp1;
        p_get_result++;
        temp1  = * p_get_result;
        p_get_result++;
        temp1 += * p_get_result;
        p_get_result++;       
        temp1 += * p_get_result;
        temp1 = temp1/3;          // get average of y
        temp1 = (temp1*ScreenHeigh)/1024 ;
        *Y    = temp1;
        return TRUE;
}

//-----------------------------------------------------------------------------
//
// Function: SetMiliSecond
//
// This function is for timer.
//
// Parameters:

//      timer
//          [in] 

// Returns:
//     NULL
//
//-----------------------------------------------------------------------------
void SetMiliSecond(cyg_uint32 timer)
{
     cyg_uint32 temp1=0;	
     cyg_uint32 temp2=0;
     cyg_uint32 time_counter=0;
     time_counter = (timer*2000)/61      ;
     temp1  = 0;
     temp1 |= 1U<<16;
     writel(temp1,EPIT1_EPITCR);
     temp1 = 0;
     temp1 |= 1U<<25;
     temp1 |= 1U<<24;         
     temp1 |= 1U<<17 ;
     temp1 |= 1;
     writel(temp1,EPIT1_EPITCR);
     writel(0xFFFFFFFF,EPIT1_EPITLR);
     writel(0,EPIT1_EPITCMPR);
     temp1 = readl(EPIT1_EPITCNR);
     do{
         temp2 = readl(EPIT1_EPITCNR);
     }while( (temp1-temp2) < time_counter);
}

bool filter_noise(cyg_uint16* data)
{
    cyg_uint16 diff1,diff2;
    int i;
    for (  i = 0; i < 6; i++ )
    {
        if(data[i]==0)
	    return FALSE;
    }
    if(data[0]>=data[1])
	diff1=data[0]-data[1];
    else
	diff1=data[1]-data[0];

    if(data[1]>=data[2])
	diff1=data[1]-data[2];
    else
	diff1=data[2]-data[1];

    if(data[2]>=data[0])
	diff1=data[2]-data[0];
    else
	diff1=data[0]-data[2];

    if(data[3]>=data[4])
	diff2=data[3]-data[4];
    else
	diff2=data[4]-data[1];

    if(data[4]>=data[5])
	diff2=data[4]-data[5];
    else
	diff2=data[5]-data[4];

    if(data[5]>=data[3])
	diff2=data[5]-data[3];
    else
	diff2=data[3]-data[5];

    if(diff1>=50||diff2>=50)
	return FALSE;
    else
        return TRUE; 	

}
//-----------------------------------------------------------------------------
//
// Function: mc13783_adc_read_ts
//
// This function places the PMIC in touch position mode and acquires
// touch samples.
//
// Parameters:

//      outbuffer
//          [out] Points to data buffer to hold touch samples.

// Returns:
//      TRUE if the successful, FALSE otherwise.
//
//-----------------------------------------------------------------------------
static bool mc13783_adc_read_ts(cyg_int8* outbuffer)
{
    bool ret = FALSE;
    cyg_uint32 addr=0, data=0, mask=0;
    cyg_int32 i=0;
    cyg_uint16* readdata = (cyg_uint16*) outbuffer;
   
    if (!pmic_adc_set_touch_mode(3))
    {
	diag_printf("mc13783_adc_read_ts error");
        goto erro;
    }
    addr = REG_ADC_1;
    data = 1<<3 |1 | 1<<1 | 1<<11 | 1<<21;
    mask =0xffffff;
    pmic_set(addr, data, mask);
    addr = REG_ADC_0;
    data=1<<10;
    mask =((1U<<1)-1)<<16|((1U<<1)-1)<<17|((1U<<1)-1)<<11|((1U<<1)-1)<<10;
    pmic_set(addr, data, mask); 
    if (mc13783_adc_convert() != PMIC_SUCCESS)
    {
 	diag_printf("mc13783_adc_convert error");
        goto erro;
    }
       
    addr = REG_ADC_0;
    data = 1<<16|1<<17;
    mask = ((1U<<1)-1)<<16|((1U<<1)-1)<<17;
    pmic_set(addr, data, mask);
    addr = REG_ADC_1;
    data =4<<8;
    mask = ((1U<<1)-1)<<20|((1U<<3)-1)<<5|((1U<<3)-1)<<8;
    pmic_set(addr, data, mask);
    for (  i = 0; i < 4; i++ )
    {
        addr = REG_ADC_2;
	data=pmic_reg(addr,data,0);

        readdata[i] =(data &(((1U<<10)-1)<<2))>>2;
        readdata[i+4] = (data &(((1U<<10)-1)<<14))>>14;
    }
 
    pmic_adc_deactive();
  
if(filter_noise(readdata)==TRUE)
    ret = TRUE;
else
    ret = FALSE;	
erro:
    pmic_adc_set_touch_mode(0);
    return ret;
}
 
//-----------------------------------------------------------------------------
//
// Function: pmic_adc_set_touch_mode
//
// This function sets the mode for the  pmic adc.
//
// Parameters:
//      mode
//          [in]
//
// Returns:
//      TRUE if the successful, FALSE otherwise.
//
//-----------------------------------------------------------------------------

static bool pmic_adc_set_touch_mode(cyg_uint8 mode)
{
    bool ret = FALSE;
    cyg_uint32 addr, data, mask;

    addr = REG_ADC_0;
    if (mode != 0)
    {
       data =mode<<12|1<<10;
    }
    else
    {
	data =mode<<12;
    }
    mask = ((1U<<3)-1)<<12|((1U<<1)-1)<<11|((1U<<1)-1)<<10;
    pmic_set(addr, data, mask);
    if (mode == 1)
    {
        
        addr = REG_ADC_1;

	data = 1<<3|1<<0|1<<1|1<<11|1<<21;
	mask =0xffffff;
        pmic_set(addr, data, mask);
      
        if (mc13783_adc_convert() != PMIC_SUCCESS)
        {
            goto erro;
        }

    }
    ret = TRUE;
erro:
    return ret;
}



//-----------------------------------------------------------------------------
//
// Function: mc13783_adc_convert
//
// This function triggers a new set of adc conversions and waits for the
// conversions to complete.
//
// Parameters:
//      NULL
// Returns:
//      Status.
//
//-----------------------------------------------------------------------------

static PMIC_STATUS mc13783_adc_convert(void)
{
    PMIC_STATUS ret = PMIC_ERROR;
    cyg_uint32 addr, data, mask;
    cyg_int32  trytimes =0;
    addr = REG_ADC_1;
    data = 1<<20;
    mask = ((1U<<1)-1)<<20;
    pmic_set(addr, data, mask);
    trytimes = 1500;
    do 
    {
	data=pmic_reg(addr,data,0);
    } while ((data & mask) && --trytimes);
    if (!trytimes)
    {  
        goto erro;
    }
    ret = PMIC_SUCCESS;

erro:
    return ret;

}

//-----------------------------------------------------------------------------
//
// Function: pmic_adc_deactive
//
// This function is just a helper function that disables ADC conversions 
//
// Parameters:
//
// Returns:
//
//-----------------------------------------------------------------------------
static void pmic_adc_deactive(void)
{
    cyg_uint32 addr, data, mask;
  
    addr = REG_ADC_1;
    data = 0;
    mask = ((1U<<1)-1)<<0;
    pmic_set(addr, data, mask);
}



RedBoot_cmd("touch",
            "touch panel on\nSyntax:",
            "mxc touch",
             touchpanel
           );
extern cyg_uint8 mxc_kpp_scan(void);
extern void main_menu(void);
extern void choice_menu(int, int);
extern cyg_bool check_power_key(void);
extern bool exit_diagnostic;
extern void reboot_test(bool isFirst);

void touchpanel(int argc,char *argv[])
{
#define USE_TOUCH_PANEL	0

#if USE_TOUCH_PANEL
    cyg_uint16 x,y;
    cyg_int32 i;
    cyg_int32 g_choice=1;
#else
	cyg_uint32 stat;
	cyg_uint8 key;
	static int kpos=1;
#endif

	exit_diagnostic = false;

#if USE_TOUCH_PANEL
  	GetTouchPanelPoint(&x,&y);
	do
	{	

		if(GetTouchPanelPoint(&x,&y)==TRUE)
		{
			i=y/16-2;

			diag_printf("x=%d y=%d i=%d\n",x,y,i);
			
			if(i<=end_)
			{	
				g_choice = i;
				choice_menu(g_choice,1);
			}
			SetMiliSecond(50);

		}
 
	SetMiliSecond(50);
	}while(!exit_diagnostic);
	
#else

	stat = pmic_reg(22,0,0);
	if ( stat == 0x7ff0 ) {
		reboot_test(false);
		return;
	}
	
	choice_menu(kpos,0);

	SetMiliSecond(200);
	
	while(!exit_diagnostic) {	
		
		if(check_power_key()) {
			SetMiliSecond(150);
			choice_menu(kpos,1);
		}
		
		key = mxc_kpp_scan();

		if (key == 0x0) {
			// no key
			continue;
		}else if ((key&0x8) == 0x8) {
			// up key
			kpos--;
			if(kpos<1){
				kpos=1;
				continue;
			}
		} else if ((key&0x10) == 0x10) {
			// down key
			kpos++;
			if(kpos>end_){
				kpos--;
				continue;
			}
		}
		
		choice_menu(kpos,0);

		SetMiliSecond(150);	
	}
#endif
}

void pmic_set(cyg_uint32 addr, cyg_uint32 data, cyg_uint32 mask)
{
    cyg_uint32 temp;    

    temp=pmic_reg(addr,0,0);
    pmic_reg(addr,(temp & (~mask)) | (data & mask),1);
    return;
}



