/******************************************************************************
 *
 * @brief     This file is part of the TouchGFX 4.3.0 evaluation distribution.
 *
 * @author    Draupner Graphics A/S <http://www.touchgfx.com>
 *
 ******************************************************************************
 *
 * @section Copyright
 *
 * Copyright (C) 2014 Draupner Graphics A/S <http://www.touchgfx.com>.
 * All rights reserved.
 *
 * TouchGFX is protected by international copyright laws and the knowledge of
 * this source code may not be used to write a similar product. This file may
 * only be used in accordance with a license and should not be re-
 * distributed in any way without the prior permission of Draupner Graphics.
 *
 * This is licensed software for evaluation use, any use must strictly comply
 * with the evaluation license agreement provided with delivery of the
 * TouchGFX software.
 *
 * The evaluation license agreement can be seen on www.touchgfx.com
 *
 * @section Disclaimer
 *
 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Draupner Graphics A/S has
 * no obligation to support this software. Draupner Graphics A/S is providing
 * the software "AS IS", with no express or implied warranties of any kind,
 * including, but not limited to, any implied warranties of merchantability
 * or fitness for any particular purpose or warranties against infringement
 * of any proprietary rights of a third party.
 *
 * Draupner Graphics A/S can not be held liable for any consequential,
 * incidental, or special damages, or any other relief, or for any claim by
 * any third party, arising from your use of this software.
 *
 *****************************************************************************/
 
 /**
 * Revision:
 * Port 8080 base MIPI LCD and SSD196x TFT controller to Touchgfx 4.4.1
 * Programmer: John Leung
 * Date: 3rd Jan 2016
*/
 
#include <common/TouchGFXInit.hpp>
#include <touchgfx/hal/BoardConfiguration.hpp>
#include <platform/hal/ST/mcu/stm32f4x9/STM32F4HAL.hpp>
#include <platform/hal/ST/mcu/stm32f4x9/STM32F4DMA.hpp>
#include <touchgfx/hal/NoDMA.hpp>
#include <touchgfx/hal/GPIO.hpp>
#include <bsp/ResistiveTouchController.hpp>
#include <platform/core/arm/cortex-m/CortexMMCUInstrumentation.hpp>
#include <platform/driver/LCD/LCD16bpp.hpp>
#include <platform/driver/touch/NoTouchController.hpp>

extern "C" {
  #include "stm32f4xx.h"
  #include "stm32f429i_discovery.h"
  #include "stm32f429i_discovery_lcd.h"
	
	//New for 240x240 iPod Nano6 LCD porting
	#ifdef USE_SSD2805_S6D04D2
	#include "LCDControllerS6D04D2.hpp"
	#include "SSD2541TouchController.hpp"
	#elif defined (	USE_SSD196X)
		#include "LCDControllerSSD196x.hpp"
	#endif
}

#ifndef FRAME_BUFFER_START
	static uint32_t frameBuf0 = (uint32_t)(0xd0000000);
#else
	static uint32_t frameBuf0 = FRAME_BUFFER_START;	//defined in graphic controller header e.g. LCDControllerxxx.hpp
	//static uint32_t frameBuf0[28800] __attribute__((at(FRAME_BUFFER_START)));
#endif

static void LCD_Config(void);
static void LCD_AF_GPIOConfig(void);

/**
  * @brief LCD configuration.
  * @note  This function Configure tha LTDC peripheral :
  *        1) Configure the Pixel Clock for the LCD
  *        2) Configure the LTDC Timing and Polarity
  *        3) Configure the LTDC Layer 1 :
  *           - The frame buffer is located at FLASH memory
  *           - The Layer size configuration : 240x160
  *        4) Configure the LTDC Layer 2.
  *           - The frame buffer is located at FLASH memory
  *           - The Layer size configuration : 240x160
  * @retval
  *  None
  */
static void LCD_Config(void)
{
  LTDC_InitTypeDef               LTDC_InitStruct;
  LTDC_Layer_InitTypeDef         LTDC_Layer_InitStruct;
  GPIO_InitTypeDef               GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

  /* Configure NCS in Output Push-Pull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOD, &GPIO_InitStructure);

  /* Configure the LCD Control pins ------------------------------------------*/
  LCD_CtrlLinesConfig();
  LCD_ChipSelect(DISABLE);
  LCD_ChipSelect(ENABLE);

  /* Configure the LCD_SPI interface -----------------------------------------*/
  LCD_SPIConfig();

  /* Power on the LCD --------------------------------------------------------*/
  LCD_PowerOn();

  /* Enable the LTDC Clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_LTDC, ENABLE);

  /* Enable the DMA2D Clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2D, ENABLE);

  /* Configure the LCD Control pins */
  LCD_AF_GPIOConfig();

  /* Configure the FMC Parallel interface : SDRAM is used as Frame Buffer for
     LCD */
  SDRAM_Init();

  /* Enable Pixel Clock ------------------------------------------------------*/

  /* Configure PLLSAI prescalers for LCD */
  /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */
  /* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 192 Mhz */
  /* PLLLCDCLK = PLLSAI_VCO Output/PLLSAI_R = 192/3 = 64 Mhz */
  /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = 64/8 = 8 Mhz */
  RCC_PLLSAIConfig(192, 7, 7);
  RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4);

  /* Enable PLLSAI Clock */
  RCC_PLLSAICmd(ENABLE);
  /* Wait for PLLSAI activation */
  while(RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET)
  {
  }

  /* LTDC Initialization -----------------------------------------------------*/

  /* Initialize the horizontal synchronization polarity as active low*/
  LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL;
  /* Initialize the vertical synchronization polarity as active low */
  LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL;
  /* Initialize the data enable polarity as active low */
  LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL;
  /* Initialize the pixel clock polarity as input pixel clock */
  LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IPC;

  /* Timing configuration */
  /* Configure horizontal synchronization width */
  LTDC_InitStruct.LTDC_HorizontalSync = 9;
  /* Configure vertical synchronization height */
  LTDC_InitStruct.LTDC_VerticalSync = 1;
  /* Configure accumulated horizontal back porch */
  LTDC_InitStruct.LTDC_AccumulatedHBP = 29;
  /* Configure accumulated vertical back porch */
  LTDC_InitStruct.LTDC_AccumulatedVBP = 3;
  /* Configure accumulated active width */
  LTDC_InitStruct.LTDC_AccumulatedActiveW = 269;
  /* Configure accumulated active height */
  LTDC_InitStruct.LTDC_AccumulatedActiveH = 323;
  /* Configure total width */
  LTDC_InitStruct.LTDC_TotalWidth = 279;
  /* Configure total height */
  LTDC_InitStruct.LTDC_TotalHeigh = 327;

  LTDC_Init(&LTDC_InitStruct);

  /* Configure R,G,B component values for LCD background color */
  LTDC_InitStruct.LTDC_BackgroundRedValue = 0;
  LTDC_InitStruct.LTDC_BackgroundGreenValue = 0;
  LTDC_InitStruct.LTDC_BackgroundBlueValue = 0;

  LTDC_Init(&LTDC_InitStruct);

  /* LTDC initialization end -------------------------------------------------*/


    /* Layer1 Configuration ----------------------------------------------------*/

  /* Windowing configuration */
  /* In this case all the active display area is used to display a picture then:
  Horizontal start = horizontal synchronization + Horizontal back porch = 30
  Horizontal stop = Horizontal start + window width -1 = 30 + 240 -1
  Vertical start   = vertical synchronization + vertical back porch     = 4
  Vertical stop   = Vertical start + window height -1  = 4 + 160 -1      */
  LTDC_Layer_InitStruct.LTDC_HorizontalStart = 30;
  LTDC_Layer_InitStruct.LTDC_HorizontalStop = (240 + 30 - 1);
  LTDC_Layer_InitStruct.LTDC_VerticalStart = 4;
  LTDC_Layer_InitStruct.LTDC_VerticalStop = 320 + 4 -1;

  /* Pixel Format configuration*/
  LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565;

  /* Alpha constant (255 totally opaque) */
  LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 255;

  /* Configure blending factors */
  LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA;
  LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA;

  /* Default Color configuration (configure A,R,G,B component values) */
  LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0;
  LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0;
  LTDC_Layer_InitStruct.LTDC_DefaultColorRed = 0;
  LTDC_Layer_InitStruct.LTDC_DefaultColorAlpha = 0;

  /* Input Address configuration */
	#ifndef USE_SSD2805_S6D04D2
  LTDC_Layer_InitStruct.LTDC_CFBStartAdress = frameBuf0;
	#endif
	
  /* the length of one line of pixels in bytes + 3 then :
  Line Lenth = Active high width x number of bytes per pixel + 3
  Active high width         = 240
  number of bytes per pixel = 2    (pixel_format : RGB565)
  */
  LTDC_Layer_InitStruct.LTDC_CFBLineLength = ((240 * 2) + 3);

  /*  the pitch is the increment from the start of one line of pixels to the
  start of the next line in bytes, then :
  Pitch = Active high width x number of bytes per pixel
  */
  LTDC_Layer_InitStruct.LTDC_CFBPitch = (240 * 2);

  /* configure the number of lines */
  LTDC_Layer_InitStruct.LTDC_CFBLineNumber = 320;

  LTDC_LayerInit(LTDC_Layer1, &LTDC_Layer_InitStruct);

  /* Layer1 Configuration end ------------------------------------------------*/

  LTDC_DitherCmd(ENABLE);
}

/**
  * @brief GPIO config for LTDC.
  * @retval
  *  None
  */
static void LCD_AF_GPIOConfig(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  /* Enable GPIOI, GPIOJ, GPIOG, GPIOF, GPIOH AHB Clocks */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | \
    RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | \
      RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG, ENABLE);

  /* GPIOs Configuration */
  /*
  +------------------------+-----------------------+----------------------------+
  +                       LCD pins assignment                                   +
  +------------------------+-----------------------+----------------------------+
  |  LCD_TFT R2 <-> PC.12  |  LCD_TFT G2 <-> PA.06 |  LCD_TFT B2 <-> PD.06      |
  |  LCD_TFT R3 <-> PB.00  |  LCD_TFT G3 <-> PG.10 |  LCD_TFT B3 <-> PG.11      |
  |  LCD_TFT R4 <-> PA.11  |  LCD_TFT G4 <-> PB.10 |  LCD_TFT B4 <-> PG.12      |
  |  LCD_TFT R5 <-> PA.12  |  LCD_TFT G5 <-> PB.11 |  LCD_TFT B5 <-> PA.03      |
  |  LCD_TFT R6 <-> PB.01  |  LCD_TFT G6 <-> PC.07 |  LCD_TFT B6 <-> PB.08      |
  |  LCD_TFT R7 <-> PG.06  |  LCD_TFT G7 <-> PD.03 |  LCD_TFT B7 <-> PB.09      |
  -------------------------------------------------------------------------------
  |  LCD_TFT HSYNC <-> PC.06  | LCDTFT VSYNC <->  PA.04 |
  |  LCD_TFT CLK   <-> PG.07  | LCD_TFT DE   <->  PF.10 |
  -----------------------------------------------------

  */

  /* GPIOA configuration */
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_LTDC);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_6 | \
                             GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* GPIOB configuration */
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource0,  0x09);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, 0x09);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_LTDC);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | \
                             GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;

  GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* GPIOC configuration */
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_LTDC);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_10;

  GPIO_Init(GPIOC, &GPIO_InitStruct);

  /* GPIOD configuration */
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource3, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_LTDC);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_6;

  GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* GPIOF configuration */
  GPIO_PinAFConfig(GPIOF, GPIO_PinSource10, GPIO_AF_LTDC);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;

  GPIO_Init(GPIOF, &GPIO_InitStruct);

  /* GPIOG configuration */
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource6, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource7, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource10, 0x09);
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_LTDC);
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource12, 0x09);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_10 | \
                             GPIO_Pin_11 | GPIO_Pin_12;

  GPIO_Init(GPIOG, &GPIO_InitStruct);
}

namespace touchgfx
{
	NoDMA noDMA;	//new - TechToys
  STM32F4DMA dma;
#ifdef USE_SSD2805_S6D04D2
	SSD2541TouchController tc(240,240);
#else
  ResistiveTouchController tc;
#endif
  CortexMMCUInstrumentation mcuInstr;
  LCD16bpp display;


/*	
#ifdef USE_SSD2805_S6D04D2

class MyHAL : public STM32F4HAL
	{
	  public :

		MyHAL(touchgfx::DMA_Interface& dma, touchgfx::LCD& display, touchgfx::TouchController& tc, uint16_t width, uint16_t height) : STM32F4HAL(dma, display, tc, width, height)
		{
			touchgfx::lcd.init();
    }
		void flushFrameBuffer(void)
		{
			STM32F4HAL::flushFrameBuffer();
			touchgfx::lcd.flushFrameBuffer();
			//displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, (uint32_t)0xD0000000);	
		}
		void flushFrameBuffer(const Rect& rect)
		{
			STM32F4HAL::flushFrameBuffer(rect);
			touchgfx::lcd.flushFrameBuffer();
			//displayXferFB(rect.x, rect.y, rect.width, rect.height, (uint32_t)0xD0000000);
			//displayXferFB(0, 0, DISP_HOR_RESOLUTION, DISP_VER_RESOLUTION, (uint32_t)0xD0000000);
		}
		private:	
	};
#endif
*/	
  void touchgfx_init()
  {
#ifdef USE_SSD2805_S6D04D2
		//MyHAL& hal= (MyHAL&)touchgfx_generic_init<MyHAL>(noDMA, display, tc, 240, 240, 0, 0);
		LCDController& hal = (LCDController&)touchgfx_generic_init<LCDController>(noDMA, display, tc, 240, 240, 0, 0);
		hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, false, false);	
		// This platform can handle simultaneous DMA and TFT accesses to SDRAM, so disable lock to increase performance.
		hal.lockDMAToFrontPorch(false); 
#elif defined (USE_SSD196X)
		#ifdef USE_TY700TFT800480
		LCDController& hal = (LCDController&)touchgfx_generic_init<LCDController>(noDMA, display, tc, 800, 480, 0, 0);
		#elif defined USE_TY430TFT480272
		LCDController& hal = (LCDController&)touchgfx_generic_init<LCDController>(noDMA, display, tc, 480, 272, 0, 0);
		#endif
		hal.setFrameBufferStartAddress((uint16_t*)frameBuf0, 16, false, false);	
		hal.lockDMAToFrontPorch(false); 	
#else
		HAL& hal = touchgfx_generic_init<STM32F4HAL>(dma, display, tc, 240, 320, 0, 0);
		hal.setFrameBufferStartAddress((uint16_t*)frameBuf0);
#endif
     
    hal.setTouchSampleRate(1);   
    mcuInstr.init();

    //Set MCU instrumentation and Load calculation
    hal.setMCUInstrumentation(&mcuInstr);
    hal.enableMCULoadCalculation(true);
  }

  void hw_init()
  {
#if defined (USE_SSD2805_S6D04D2) || defined (USE_SSD196X)
	  /* Configure the FMC Parallel interface : SDRAM is used as Frame Buffer for TouchGFX */
		SDRAM_Init();
		//displayInit();		//8080 LCD initialization	
		
		/**
		Heart rate sensor init
		*/
		/*
		GPIO_InitTypeDef GPIO_InitStruct;
	
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;	//output push-pull mode
		GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
		GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
		GPIO_Init(GPIOG, &GPIO_InitStruct);
		GPIO_WriteBit(GPIOG, GPIO_Pin_2, Bit_SET);	
		*/
#else
    /* Configure LCD */
    LCD_Config();

    /* Enable Layer 1 */
    LTDC_LayerCmd(LTDC_Layer1, ENABLE);

    /* Reload LTDC configuration  */
    LTDC_ReloadConfig(LTDC_IMReload);

    /* Enable The LCD */
    LTDC_Cmd(ENABLE);

    /* Set LCD foreground layer */
    LCD_SetLayer(LCD_FOREGROUND_LAYER);
    GPIO::init();		
#endif


  }

}



