/* 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 the use of the 2D addressing mode on the LCD controller.
 *  The virtual frame buffer is two times the size of the LCD viewing area.
 *  Before you run this application program the picture (AVR32.bmp) into the flash at
 *  address 0x00400000. Use the avr32program application for this purpose.
 *  avr32program program -F bin -O 0x00400000 -evfcfi@0 AVR32.bmp
 *  If you intend to program the picture to another location change the define
 *  BITMAP_FILE_ADDRESS accordingly.
 *
 *  The input (switches) header marked J25, used for moving around the virtual frame buffer,
 *  must be connected to the header labeled J1 (PORTB[0..7]).
 *
 *  To move the viewing area use following switches =
 *  Switch0 = Move viewing area 10 pixels to the right
 *  Switch1 = Move viewing area 10 pixels to the left
 *  Switch2 = Move viewing area 10 lines up
 *  Switch3 = Move viewing area 10 lines down
 *
 * - 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/
 *
 *****************************************************************************/

/* Copyright (C) 2006-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 the use of the 2D addressing mode on the LCD controller. It uses the TFT
 *  display on the STK1000 to show a part of the "virtual" framebuffer. The virtual 
 *  frame buffer (640*480) is four times the size of the LCD viewing area (320*240). 
 *  A bitmap picture (320*240) will read from the flash, enlarged by four and then written to 
 *  the framebuffer. So you will only see a part of the picture on the screen. This viewing area can
 *  be moved around the "virtual" framebuffer with switches on the STK1000.
 * 
 *  Before you run this application program the picture (AVR32.bmp) must be written into the flash at 
 *  address 0x00400000. Use the avr32program application for this purpose.
 *  avr32program program -F bin -O 0x00400000 -evfcfi\@0 AVR32.bmp
 *  If you intend to program the picture to another location change the define 
 *  BITMAP_FILE_ADDRESS accordingly in the source. Feel free to use your own bitmap file.
 * 
 *  The input (switches) header marked \b J25, used for moving around the virtual frame buffer,
 *  must be connected to the header labeled \b J1 (PORTB[0..7]).
 * 
 *  To move the viewing area use following switches:
 *  Switch0: Move viewing area 10 pixels to the right
 *  Switch1: Move viewing area 10 pixels to the left
 *  Switch2: Move viewing area 10 lines up
 *  Switch3: Move viewing area 10 lines down
 *
 *  - \b SRC = lcdc_moving_bitmap_example.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 <string.h>
#include "usart.h"
#include "gpio.h"
#include "spi_at32ap7000.h"
#include "lcdc.h"
#include "board.h"
#include "ltv350qv.h"
#include "bmp_lib.h"
#include "print_funcs.h"
#include "pm_at32ap7000.h"
#include "sdramc_at32ap7000.h"

extern void usdelay(unsigned long usec);

int increment_frame_base(lcdc_conf_t *lcdc_conf, int pixel, int line);
void lcd_pio_config(void);
void init_spiMaster(volatile avr32_spi_t * spi, long cpuHz);

#define SWITCHES_MASK 0x000000FF
#define SWITCH0 0x00000001
#define SWITCH1 0x00000002
#define SWITCH2 0x00000004
#define SWITCH3 0x00000008

/*! \brief Start address of the bitmap file */
#define BITMAP_FILE_ADDRESS 0x00400000

/*! \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_ON,
  .virtual_xres = 640,
  .virtual_yres = 480,
  .frame_rate = 75,
  .lcdcclock = 40000000,
  .guard_time = 2,
  .memor = LCDC_BIG_ENDIAN,
  .ifwidth = 0,
  .scanmod = LCDC_SINGLE_SCAN,
  .distype = LCDC_TFT,
  .invvd = LCDC_NORMAL,
  .invframe = LCDC_INVERTED,
  .invline = LCDC_INVERTED,
  .invclk = LCDC_INVERTED,
  .invdval = LCDC_INVERTED,
  .clkmod = LCDC_ALWAYS_ACTIVE,
  .pixelsize = LCDC_BPP_32,
  .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 Initialise SPI in master mode for the LCD
 *
 *  \param spi Pointer to the correct avr32_spi_t struct
 *  \param cpuHz the CPU clock frequency.
 */
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 Sets up the pins for the LCD on the STK1000
 *
 */
void lcd_pio_config(void){
    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 Move the viewing area in the virtual frame buffer
 *
 *  \param lcdc_conf Pointer to the LCD controller configuration
 *  \param pixel Number of pixels to move the viewing area
 *  \param line Number of lines to move the viewing area
 *
 *  \return Returns the status of the movement
 *    \retval 0 Movement succeeded
 *    \retval -1 Movement was not successfull (viewing are is out of the
 *               virtual frame buffer
 */
int increment_frame_base(lcdc_conf_t *lcdc_conf, int pixel, int line){

  volatile avr32_lcdc_t *plcdc = &AVR32_LCDC;
  unsigned long base1;

  /* increment frame pointer by lines */
  base1 = plcdc->dmabaddr1 + lcdc_conf->virtual_xres * lcdc_conf->pixelsize / 8 * line;
  /* increment frame pointer by pixel */
  base1 += 4 * pixel;

  /* do not allow to move the viewing area out of the virtual frame buffer */
  if((base1 >= lcdc_conf->dmabaddr1 ) &&
      base1 <= (lcdc_conf->dmabaddr1 + lcdc_conf->virtual_xres * lcdc_conf->pixelsize / 8 * (lcdc_conf->virtual_yres - lcdc_conf->yres) ))
    plcdc->dmabaddr1 = base1;
  else
    return -1;

  /* update DMA configuration DMAUPDT */
  plcdc->dmacon |=  (1 << AVR32_LCDC_DMACON_DMAUPDT_OFFSET);
  return 0;
}

/*! \brief 2D addressing mode example for the STK1000
 *  Connect switches pins to J1.
 *  Switch0 = Move viewing area 10 pixels to the right
 *  Switch1 = Move viewing area 10 pixels to the left
 *  Switch2 = Move viewing area 10 lines up
 *  Switch3 = Move viewing area 10 lines down
 */
int main (void)
{
  volatile avr32_spi_t * spi = &AVR32_SPI0;
  unsigned int input;
  volatile avr32_pio_t *piob = &AVR32_PIOB;

  // 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 debug serial line */
  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));

  piob->per = SWITCHES_MASK;
  piob->codr = SWITCHES_MASK;
  lcd_pio_config();

  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");

  print_dbg("Enabling LCD controller\n");
  /* Power manager setup
  * 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);

  print_dbg("Initializing LCD controller\n");
  lcdc_init(&ltv350qv_conf);

  print_dbg("Clearing the frame buffer\n");
  memset((void *)ltv350qv_conf.dmabaddr1, 0, ltv350qv_conf.virtual_xres * ltv350qv_conf.virtual_yres * ltv350qv_conf.pixelsize / 8);

  print_dbg("Filling the frame buffer\n");
  /* print the image into the virtual framebuffer */
  display_virtual_bm(&ltv350qv_conf, ((void *) BITMAP_FILE_ADDRESS));

  while(1){

    usdelay(100000);
    /* get input from the switches */
    input = ~( piob->pdsr & SWITCHES_MASK);

    if(input & SWITCH0){
      increment_frame_base(&ltv350qv_conf, 10, 0);
    }
    if(input & SWITCH1){
      increment_frame_base(&ltv350qv_conf, -10, 0);
    }
    if(input & SWITCH2){
      increment_frame_base(&ltv350qv_conf, 0, -10);
    }
    if(input & SWITCH3){
      increment_frame_base(&ltv350qv_conf, 0, 10);
    }
  }
}

