/**
 * @file	kpd_scan_functions.c
 *
 * Implementation of hardware keypad interface functions.
 * These functions implement the keypad interface with the windows.
 *
 * @author	Laurent Sollier (l-sollier@ti.com)
 * @version 0.1
 */

/*
 * History:
 *
 *	Date       	Author       Modification
 *  ------------------------------------
 *  10/10/2001 L Sollier    Create
 *
 *
 * (C) Copyright 2001 by Texas Instruments Incorporated, All Rights Reserved
 */
#include "board.cfg"

#include "nucleus.h" /* used for HISR */

#include "kpd/kpd_scan_functions.h"
#include "kpd/kpd_cfg.h"
#include "kpd/kpd_physical_key_def.h"
#include "kpd/kpd_messages_i.h"
#include "kpd/kpd_env.h"

#include "rvf/rvf_api.h"
#include "rvm/rvm_use_id_list.h"
#include "rvf/rvf_env.h"

#if (CHIPSET == 12)
  #include "inth/sys_inth.h"
#else
  #include "armio/armio.h"
  #include "inth/iq.h"
#endif

#include "memif/mem.h"


#if (CHIPSET == 12)
  #define KBR_CTRL_REG              (MEM_KEYBOARD + 0x00) /* KBR control reg */
  #define KBR_DEBOUNCING_TIME       (MEM_KEYBOARD + 0x02) /* KBR debouncing time reg */
  #define KBR_STATE_MACHINE_STATUS  (MEM_KEYBOARD + 0x0E) /* KBR state machine status reg */
  #define KBR_IN                    (MEM_KEYBOARD + 0x10) /* KBR inputs (rows) */
  #define KBR_OUT                   (MEM_KEYBOARD + 0x12) /* KBR outputs (columns) */
#endif


#if (CHIPSET == 12)
  #define KP_ROW_IN   KBR_IN
  #define KP_COL_OUT  KBR_OUT
#else
  #define KP_ROW_IN   ARMIO_KBR_IN
  #define KP_COL_OUT  ARMIO_KBR_OUT
#endif


#if (BOARD == 7)
   #define KP_ROWS      5
   #define KP_COLS      4

   const T_KPD_PHYSICAL_KEY_ID keypad_layout[KP_ROWS][KP_COLS]=
   {                           
   /* Layout of B-Sample */
   {KPD_PKEY_SOFT_LEFT,  KPD_PKEY_UP,   KPD_PKEY_DOWN, KPD_PKEY_SOFT_RIGHT},
   {KPD_PKEY_1,          KPD_PKEY_2,    KPD_PKEY_3,    KPD_PKEY_GREEN},
   {KPD_PKEY_4,          KPD_PKEY_5,    KPD_PKEY_6,    KPD_PKEY_NULL},
   {KPD_PKEY_7,          KPD_PKEY_8,    KPD_PKEY_9,    KPD_PKEY_NULL},
   {KPD_PKEY_STAR,       KPD_PKEY_0,    KPD_PKEY_DIESE,KPD_PKEY_NULL},
   };
#elif ((BOARD == 8) || (BOARD == 9))
   #define KP_ROWS      5
   #define KP_COLS      4
   const T_KPD_PHYSICAL_KEY_ID keypad_layout[KP_ROWS][KP_COLS]=
   {                           
   /* Layout of C-Sample */
   {KPD_PKEY_UP,    KPD_PKEY_GREEN,KPD_PKEY_SOFT_RIGHT,KPD_PKEY_DOWN},
   {KPD_PKEY_1,     KPD_PKEY_2,    KPD_PKEY_3,         KPD_PKEY_SOFT_LEFT},
   {KPD_PKEY_4,     KPD_PKEY_5,    KPD_PKEY_6,         KPD_PKEY_NULL},
   {KPD_PKEY_7,     KPD_PKEY_8,    KPD_PKEY_9,         KPD_PKEY_NULL},
   {KPD_PKEY_STAR,  KPD_PKEY_0,    KPD_PKEY_DIESE,     KPD_PKEY_NULL},
   };
#elif ((BOARD == 40) || (BOARD == 41) || (BOARD == 42) || (BOARD == 43))
   #define KP_ROWS      5
   #define KP_COLS      5
   const T_KPD_PHYSICAL_KEY_ID keypad_layout[KP_ROWS][KP_COLS]=
   {                           
   /* Layout of D-Sample and E-Sample */
   {KPD_PKEY_GREEN, KPD_PKEY_VOL_DOWN, KPD_PKEY_VOL_UP,KPD_PKEY_SOFT_LEFT,  KPD_PKEY_LEFT},
   {KPD_PKEY_1,     KPD_PKEY_2,        KPD_PKEY_3,     KPD_PKEY_REC,        KPD_PKEY_RIGHT},
   {KPD_PKEY_4,     KPD_PKEY_5,        KPD_PKEY_6,     KPD_PKEY_SOFT_RIGHT, KPD_PKEY_UP},
   {KPD_PKEY_7,     KPD_PKEY_8,        KPD_PKEY_9,     KPD_PKEY_NULL,       KPD_PKEY_DOWN},
   {KPD_PKEY_STAR,  KPD_PKEY_0,        KPD_PKEY_DIESE, KPD_PKEY_NULL,       KPD_PKEY_NAV_CENTER},
   };
#endif

#define KP_ACTIVATE(i)  (~(1<<i))
#define KP_IS_ACTIVE(rows,i)  ((rows & (1<<i)) == 0)
#define KP_ALL_OFF  0x1F
#define KP_ALL_ON   0

extern T_KPD_ENV_CTRL_BLK* kpd_env_ctrl_blk;

typedef struct {  NU_HISR  hisr;
                  char     hisr_stack[512];
               } T_HISR_INFOS;

static T_HISR_INFOS hisr_infos = {0};


/**
 * @name Functions implementation
 *
 */
/*@{*/


#if (CHIPSET == 12)

/**  kpd_init_ctrl_reg : Initialize the Control register
 */
void kpd_init_ctrl_reg(const UINT8 software_nreset, 
                       const T_KPD_Nsoftware_mode nsoftware_mode, 
                       const T_KPD_PTV ptv,
                       const T_KPD_EnableDetection long_key_process_en,
                       const T_KPD_EnableDetection time_out_empty_en,
                       const T_KPD_EnableDetection time_out_long_key_en, 
                       const T_KPD_EnableDetection repeat_mode_en)
{
  volatile UINT16 status_reg;
  status_reg = *(volatile UINT16*) KBR_STATE_MACHINE_STATUS;

  if ( (status_reg != KPD_TEST_TIMER_DEBOUNCING) && (status_reg != KPD_TEST_TIMER_LONG_KEY) &&
       (status_reg != KPD_TEST_TIMER_TIME_OUT)   && (status_reg != KPD_TEST_TIMER_REPEAT_KEY) )
  {
    
    /* The PTV can be programmed since the timer is not running */
    
    *(volatile UINT16*) KBR_CTRL_REG = (software_nreset |
                                        nsoftware_mode       << 1 |
                                        ptv                  << 2 |
                                        long_key_process_en  << 5 |
                                        time_out_empty_en    << 6 |
                                        time_out_long_key_en << 7 |
                                        repeat_mode_en       << 8);
  }
  else
  {
    
    /* The PTV must not be programmed when the timer is running */

    SetGroupBits16(*(volatile unsigned short *)(KBR_CTRL_REG), 0, 1, software_nreset);
    SetGroupBits16(*(volatile unsigned short *)(KBR_CTRL_REG), 1, 1, nsoftware_mode);
    SetGroupBits16(*(volatile unsigned short *)(KBR_CTRL_REG), 5, 1, long_key_process_en);
    SetGroupBits16(*(volatile unsigned short *)(KBR_CTRL_REG), 6, 1, time_out_empty_en);
    SetGroupBits16(*(volatile unsigned short *)(KBR_CTRL_REG), 7, 1, time_out_long_key_en);
    SetGroupBits16(*(volatile unsigned short *)(KBR_CTRL_REG), 8, 1, repeat_mode_en);
  }
}


/**  kpd_software_reset : reset software  
 */
void kpd_software_reset(void)
{
  volatile UINT16 mem_reg;
  
  mem_reg = (*(volatile UINT16*) KBR_CTRL_REG) & 0xFFFE;
  *(volatile UINT16*) KBR_CTRL_REG = mem_reg;

}


/**  kpd_set_debouncing_time : Set the desired value of debouncing time
 */
void kpd_set_debouncing_time(const UINT8 debouncing_time)
{

   *(volatile UINT16*) KBR_DEBOUNCING_TIME = debouncing_time;
  
}

#endif /* #if (CHIPSET == 12) */




/**
 * function: hisr_entry
 */
static void hisr_entry(void)
{
   T_RVF_MB_STATUS mb_status;
   T_KPD_KEY_PRESSED_MSG* msg_key_pressed;
   T_KPD_PHYSICAL_KEY_ID key;

   /* Reserve memory for message */
   mb_status = rvf_get_buf (kpd_env_ctrl_blk->prim_id, sizeof(T_KPD_KEY_PRESSED_MSG), (void **) &msg_key_pressed);	

   if (mb_status != RVF_RED) /* Memory allocation success */
   {
      /* Fill the message */
      msg_key_pressed->hdr.msg_id = KPD_KEY_PRESSED_MSG;
      /* Virtual key id is not yet known */
      msg_key_pressed->value = KPD_PKEY_NULL;
      if (mb_status == RVF_GREEN)
         msg_key_pressed->key_to_process = TRUE;
      else
         msg_key_pressed->key_to_process = FALSE;

      /* Send message to the keypad task */
      rvf_send_msg(kpd_env_ctrl_blk->addr_id, msg_key_pressed);
   }
   else
   {
      KPD_SEND_TRACE("KPD: Not enough memory to send new key pressed", RV_TRACE_LEVEL_ERROR);
      kpd_acknowledge_key_pressed();
   }
}


/**
 * function: kpd_initialize_keypad_hardware
 */
void kpd_initialize_keypad_hardware(void)
{
   /* HISR creation */
   NU_Create_HISR(&hisr_infos.hisr,
                  "KPD_HISR",
                  hisr_entry,
                  2,
                  hisr_infos.hisr_stack,
                  sizeof(hisr_infos.hisr_stack));


#if (CHIPSET == 12)
  /* Init control register ; hardware decoding */
   kpd_init_ctrl_reg(1, HARDWARE_DECODING, KPD_CLK_DIV32,
                     KPD_DETECTION_DISABLED, KPD_DETECTION_DISABLED,
                     KPD_DETECTION_DISABLED, KPD_DETECTION_DISABLED);

   /* Debouncing time = 64ms */
   kpd_set_debouncing_time(0x3F);

#endif

   /* Activate all outputs */
   *(volatile UINT16*) KP_COL_OUT = KP_ALL_ON;

   /* Unmask keypad interrupt */
   #if ((BOARD == 8) || (BOARD == 9) || (BOARD == 40) || (BOARD == 41))
     AI_UnmaskIT (ARMIO_MASKIT_KBD);
   #elif (CHIPSET == 12)
     F_INTH_ENABLE_ONE_IT(C_INTH_KEYBOARD_IT);
   #else
     IQ_Unmask (IQ_ARMIO);
   #endif
}



/**
 * function: kpd_key_handler
 */
void kpd_key_handler(void)
{
   /* If keypad is not started, return immediately */
   if ( (kpd_env_ctrl_blk == 0) || (kpd_env_ctrl_blk->swe_is_initialized == FALSE) )
   {
      kpd_acknowledge_key_pressed();
   }
   else
   {
      /* Mask keypad interrupt until key is released */
#if ((BOARD == 8) || (BOARD == 9) || (BOARD == 40) || (BOARD == 41))
       AI_MaskIT (ARMIO_MASKIT_KBD);
#elif (CHIPSET == 12)
       F_INTH_DISABLE_ONE_IT(C_INTH_KEYBOARD_IT);
#else
       IQ_Mask(IQ_ARMIO);
#endif

      /* Activate HISR to process the key event */
      NU_Activate_HISR(&hisr_infos.hisr);
   }
}

/**
 * function: kpd_acknowledge_key_pressed
 */
void kpd_acknowledge_key_pressed(void)
{
   /* Unmask keypad interrupt */
  #if ((BOARD == 8) || (BOARD == 9) || (BOARD == 40) || (BOARD == 41))
    AI_UnmaskIT (ARMIO_MASKIT_KBD);
  #elif (CHIPSET == 12)
     F_INTH_ENABLE_ONE_IT(C_INTH_KEYBOARD_IT);
  #else
    IQ_Unmask (IQ_ARMIO);
  #endif
}


/*
 * delay
 *
 * Wait a while to let bus settle
 * Magic value found by trial and error
 * 
 */
static void delay(void)
{
   volatile int i;

   for (i=0;i<10;i++) ;
}

/**
 * function: kpd_scan_keypad
 */
T_KPD_PHYSICAL_KEY_ID kpd_scan_keypad(void)
{
   int row, col;
   volatile UINT16 rows;

   /* Activate all columns to find if any row is active */
   *(volatile UINT16*) KP_COL_OUT = KP_ALL_ON;
   delay();

   rows = (*(volatile UINT16*) KP_ROW_IN) & 0x1F;
   if (rows == KP_ALL_OFF)
      return KPD_PKEY_NULL;

   /* Deactivate all columns */
   *(volatile UINT16*) KP_COL_OUT = KP_ALL_OFF;

   /* Activate 1 column at a time */
   for (col = 0; col < KP_COLS; col++)
   {
      *(volatile UINT16*) KP_COL_OUT = (KP_ACTIVATE(col));
      delay();

      /* Find which row is active */
      rows = (*(volatile UINT16*) KP_ROW_IN) & 0x1F;

      if (rows != KP_ALL_OFF)
      {
         for (row = 0; row < KP_ROWS; row++)
         {
            /* first active row */
            if ( KP_IS_ACTIVE(rows,row))
            {
               /* Reactivate all columns */
               *(volatile UINT16*) KP_COL_OUT = KP_ALL_ON;
               /* DO NOT remove this comment. It allows to simply define the link physical layout
                  and physical key Id (for a new keypad) */
               //KPD_SEND_TRACE_PARAM("KPD: Keypad layout check ", keypad_layout[row][col], RV_TRACE_LEVEL_DEBUG_HIGH);
               return keypad_layout[row][col];
            }
         }
      }
   }

   /* No row was active - Reactivate all columns and return */
   *(volatile UINT16*) KP_COL_OUT = KP_ALL_ON;
   return KPD_PKEY_NULL;
}


/*@}*/
