/* This source file is part of the ATMEL AVR32-SoftwareFramework-AT32AP7000-1.0.0 Release */

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief LCD controller example for the STK1000.
 *
 *  This example shows how to use the LCD controller. It sets up the LCD
 *  controller for the TFT module on the STK1000 and prints a simple test
 *  pattern onto it.
 *
 * - Compiler=           GNU GCC and IAR EWAVR32 for AVR32
 * - Supported devices=  All AVR32AP devices with a SIDSA LCD controller
 * - AppNote=            AVR32114 Using the AVR32 LCD controller
 *
 * \author               Atmel Corporation= http=//www.atmel.com \n
 *                       Support and FAQ= http=//support.atmel.no/
 *
 *****************************************************************************/

/*! \page License
 * Copyright (c) 2008, Atmel Corporation All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``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 EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 */ 

/*! \mainpage
 * \section intro Introduction
 * This is the documentation for the data structures, functions, variables,
 * defines, enums, and typedefs in the software for application note AVR32114.
 * It also describes the usage of the example applictions for the LCD controller.
 *
 * \section compinfo Compilation Info
 * This software was written for the GNU GCC for AVR32 and IAR Systems compiler
 * for AVR32. Other compilers may or may not work.
 *
 * \section deviceinfo Device Info
 * All AVR32 devices with a LCDC module can be used. This example has been tested
 * with the following setup:
 *
 * - STK1000 development kit
 * - AP7000 top board for STK1000 development kit
 * - LCD LTV350QV mounted on the STK1000
 *
 *  CPU speed: <i>40 MHz</i>
 *
 *  This example shows how to set up the LCD controller for the TFT display on the STK1000
 *  and shows a simple test pattern on the screen.
 *
 *  - \b SRC lcdc_testscreen_example.c lcdc.c
 *
 * \section contactinfo Contact Info
 * For more info about Atmel AVR32 visit
 * <A href="http://www.atmel.com/products/AVR32/" >Atmel AVR32</A> \n
 * <A href="http://www.atmel.com/dyn/products/app_notes.asp?family_id=682">
 * AVR32 Application Notes</A>\n
 * Support mail: avr32@atmel.com
 */

#include "usart.h"
#include "gpio.h"
#include "spi_at32ap7000.h"
#include "lcdc.h"
#include "board.h"
#include "ap7_utils.h"
#include <string.h>
#include "errno.h"
#include "print_funcs.h"
#include "ltv350qv.h"
#include "sdramc_at32ap7000.h"
#include "pm_at32ap7000.h"

extern void ltv350qv_power_on(volatile avr32_spi_t * spi, unsigned char chip_select);
extern int display_bm(lcdc_conf_t *lcdc_conf, void * bm_file);
extern void usdelay(unsigned long usec);
extern void usart_printHex(volatile avr32_usart_t * usart, const unsigned long n);
extern void usart_print(volatile avr32_usart_t * usart, char *str);

/*! \brief LCD controller configuration */
static lcdc_conf_t ltv350qv_conf = {
  .dmabaddr1=0x10000000,
  .dmabaddr2= 0,
  .burst_length= 4,
  .xres= 320,
  .yres= 240,
  .set2dmode= LCDC_MODE_2D_OFF,
  .virtual_xres= 320,
  .virtual_yres= 240,
  .frame_rate= 75,
  .lcdcclock= 40000000,
  .guard_time= 2,
  .memor= LCDC_BIG_ENDIAN,
  .ifwidth= 0,
  .scanmod= LCDC_SINGLE_SCAN,
  .distype= LCDC_TFT,
  .invvd= LCDC_INVERTED,
  .invframe= LCDC_INVERTED,
  .invline= LCDC_INVERTED,
  .invclk= LCDC_INVERTED,
  .invdval= LCDC_INVERTED,
  .clkmod= LCDC_ALWAYS_ACTIVE,
  .pixelsize= LCDC_BPP_24,
  .ctrstval= 0x0f,
  .ctrst_ena= LCDC_ENABLED,
  .ctrst_pol= LCDC_NORMAL,
  .ctrst_ps= LCDC_PRE_HALF,
  .mval= 0,
  .mmode= LCDC_EACH_FRAME,
  .hpw= 16,
  .hbp= 15,
  .hfp= 33,
  .vpw= 1,
  .vbp= 10,
  .vfp= 10,
  .vhdly= 0,
};


/*! \brief Sets up the pins for the LCD on the STK1000
 *
 */
void lcd_pio_config(void)
{
  static const gpio_map_t piomap= {
    { AVR32_LCDC_CC_0_0_PIN, AVR32_LCDC_CC_0_0_FUNCTION },
    { AVR32_LCDC_DVAL_0_0_PIN, AVR32_LCDC_DVAL_0_0_FUNCTION },
    { AVR32_LCDC_HSYNC_0_PIN, AVR32_LCDC_HSYNC_0_FUNCTION },
    { AVR32_LCDC_MODE_0_0_PIN, AVR32_LCDC_MODE_0_0_FUNCTION },
    { AVR32_LCDC_PCLK_0_PIN, AVR32_LCDC_PCLK_0_FUNCTION },
    { AVR32_LCDC_PWR_0_PIN, AVR32_LCDC_PWR_0_FUNCTION },
    { AVR32_LCDC_VSYNC_0_PIN, AVR32_LCDC_VSYNC_0_FUNCTION },
    { AVR32_LCDC_DATA_0_0_PIN, AVR32_LCDC_DATA_0_0_FUNCTION },
    { AVR32_LCDC_DATA_1_0_PIN, AVR32_LCDC_DATA_1_0_FUNCTION },
    { AVR32_LCDC_DATA_2_0_PIN, AVR32_LCDC_DATA_1_0_FUNCTION },
    { AVR32_LCDC_DATA_3_0_PIN, AVR32_LCDC_DATA_1_0_FUNCTION },
    { AVR32_LCDC_DATA_4_0_PIN, AVR32_LCDC_DATA_1_0_FUNCTION },
    { AVR32_LCDC_DATA_5_PIN, AVR32_LCDC_DATA_5_FUNCTION },
    { AVR32_LCDC_DATA_6_PIN, AVR32_LCDC_DATA_6_FUNCTION },
    { AVR32_LCDC_DATA_7_PIN, AVR32_LCDC_DATA_7_FUNCTION },
    { AVR32_LCDC_DATA_8_0_PIN, AVR32_LCDC_DATA_8_0_FUNCTION },
    { AVR32_LCDC_DATA_9_0_PIN, AVR32_LCDC_DATA_9_0_FUNCTION },
    { AVR32_LCDC_DATA_10_0_PIN, AVR32_LCDC_DATA_10_0_FUNCTION },
    { AVR32_LCDC_DATA_11_0_PIN, AVR32_LCDC_DATA_11_0_FUNCTION },
    { AVR32_LCDC_DATA_12_0_PIN, AVR32_LCDC_DATA_12_0_FUNCTION },
    { AVR32_LCDC_DATA_13_PIN, AVR32_LCDC_DATA_13_FUNCTION },
    { AVR32_LCDC_DATA_14_PIN, AVR32_LCDC_DATA_14_FUNCTION },
    { AVR32_LCDC_DATA_15_PIN, AVR32_LCDC_DATA_15_FUNCTION },
    { AVR32_LCDC_DATA_16_0_PIN, AVR32_LCDC_DATA_16_0_FUNCTION },
    { AVR32_LCDC_DATA_17_0_PIN, AVR32_LCDC_DATA_17_0_FUNCTION },
    { AVR32_LCDC_DATA_18_0_PIN, AVR32_LCDC_DATA_18_0_FUNCTION },
    { AVR32_LCDC_DATA_19_0_PIN, AVR32_LCDC_DATA_19_0_FUNCTION },
    { AVR32_LCDC_DATA_20_0_PIN, AVR32_LCDC_DATA_20_0_FUNCTION },
    { AVR32_LCDC_DATA_21_0_PIN, AVR32_LCDC_DATA_21_0_FUNCTION },
    { AVR32_LCDC_DATA_22_PIN, AVR32_LCDC_DATA_22_FUNCTION },
    { AVR32_LCDC_DATA_23_PIN, AVR32_LCDC_DATA_23_FUNCTION }
  };
  gpio_enable_module(piomap, 31);
}


/*! \brief Fill the frame buffer with a test picture
 * Writes some colours to the framebuffer. Framebuffer pixel size is packed 24Bit.
 * \param lcdc_conf Pointer to LCD configuration structure
 */
void fill_frame_buffer (lcdc_conf_t *lcdc_conf)
{
  U32 k,l,x,y;
  volatile U8 * framePtr = (U8 *) (lcdc_conf->dmabaddr1 | 0xA0000000);

  for (l=0; l < lcdc_conf->yres; l++){        // line
    for (k=0; k < lcdc_conf->xres; k++)    // column
    {
      x = (255 * l) / lcdc_conf->yres;
      y = (255 * k) / lcdc_conf->xres;
      *framePtr++ = x;
      *framePtr++ = y;
      *framePtr++ = 255 - (x + y) / 2;
    }
  }
}


 /*! \brief initialise SPI in master mode
 *
 *  \param spi Pointer to the correct avr32_spi_t struct
 *  \param cpuHz CPU clock frequency in Hz
 */
void init_spiMaster(volatile avr32_spi_t * spi, long cpuHz)
{
  gpio_map_t spi_piomap = {          \
    {AVR32_SPI0_SCK_0_PIN, AVR32_SPI0_SCK_0_FUNCTION},  \
    {AVR32_SPI0_MISO_0_PIN, AVR32_SPI0_MISO_0_FUNCTION},  \
    {AVR32_SPI0_MOSI_0_PIN, AVR32_SPI0_MOSI_0_FUNCTION},  \
    {AVR32_SPI0_NPCS_0_PIN, AVR32_SPI0_NPCS_0_FUNCTION},  \
    {AVR32_SPI0_NPCS_1_PIN, AVR32_SPI0_NPCS_1_FUNCTION},  \
    {AVR32_SPI0_NPCS_2_PIN, AVR32_SPI0_NPCS_2_FUNCTION},  \
    {AVR32_SPI0_NPCS_3_PIN, AVR32_SPI0_NPCS_3_FUNCTION},  \
  };
  gpio_enable_module(spi_piomap, 7);

  spi_options_t spiOptions = {
    .reg = 1,
    .baudrate = 1500000,
    .bits = 8,
    .spck_delay = 0,
    .trans_delay = 0,
    .stay_act = 1,
    .spi_mode = 3,
    .modfdis = 0,
  };

  /* Initialize as master */
  spi_initMaster(spi, &spiOptions);

  /* Set master mode; variable_ps, pcs_decode, delay */
  spi_selectionMode(spi, 0, 0, 0);

  /* Select slave chip 1 (SPI_NPCS1) */
  spi_selectChip(spi, 1);

  spi_setupChipReg(spi, &spiOptions, cpuHz);

  spi_enable(spi);
}


/*! \brief LCD test pattern example.
* Sets up the LCD controller and shows a test pattern on the screen.
*/
int main (void)
{
  volatile avr32_spi_t *spi = &AVR32_SPI0;

  // Reset PM. Makes sure we get the expected clocking after a soft reset (e.g.: JTAG reset)
  pm_reset();

  // Start PLL0 giving 80 MHz clock
  pm_pll_opt_t pll_opt = {
    .pll_id = 0,
    .mul = 4,
    .div = 1,
    .osc_id = 0,
    .count = 16,
    .wait_for_lock = 1,
  };
  pm_start_pll(&pll_opt);

  // Divide HSB by 2, PBB by 2 and PBA by 4 to keep them below maximum ratings
  pm_set_clock_domain_scaler(PM_HSB_DOMAIN, 2);
  pm_set_clock_domain_scaler(PM_PBB_DOMAIN, 2);
  pm_set_clock_domain_scaler(PM_PBA_DOMAIN, 4);
  
  pm_set_mclk_source(PM_PLL0);

  init_dbg_rs232(pm_read_module_freq_hz(PM_PBA_USART1));
  print_dbg("\nCPU running at ");
  print_dbg_ulong(pm_get_mclk_freq_hz()/1000000);
  print_dbg(" MHz\n");
  sdramc_init(pm_read_module_freq_hz(PM_PBB_HSDRAMC));
  print_dbg("Board init complete\n");

  print_dbg("Setting up SPI for LTV350QV panel\n");
  init_spiMaster(spi, pm_read_module_freq_hz(PM_PBA_SPI0));
  print_dbg("Initializing LTV350QV panel\n");
  ltv350qv_power_on(spi, 1);
  print_dbg("Setting up LCD controller\n");
  lcd_pio_config();

  // Enable CLOCK for LCDC in HSBMASK 
  pm_enable_module(PM_HSB_LCDC);

  // Enable generic clock PLL0 for LCD controller pixel clock
  pm_gen_clk_opt_t gen_clk_opt = {
    .clock_source = PM_PLL0,
    .divider = 2,
  };
  pm_start_generic_clock(7, &gen_clk_opt);

  lcdc_init(&ltv350qv_conf);

  print_dbg("Testing frame buffer\n");

  // clear framebuffer
  memset((void *)ltv350qv_conf.dmabaddr1, 0, ltv350qv_conf.xres * ltv350qv_conf.yres * ltv350qv_conf.pixelsize / 8);

  fill_frame_buffer(&ltv350qv_conf);

  while(1);
}
