/* 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 set up the LCD Controller in combination with an
 *  external analog monitor. Three configurations for the resoultions VGA 648x480,
 *  SVGA 800x600 and XVGA 1024X768 are available. Check your monitor specifications
 *  if it supports one of these configurations. If not the parameters can be
 *  adjusted (blanking, polarity ...).
 *  To choose a configuration uncomment one of the defines in the example code.
 *  \#define CONF_640_480_60 -> VGA 640x480 \@60Hz
 *  \#define CONF_800_600_60 -> SVGA 800x600 \@60Hz
 *  \#define CONF_1024_768_60 -> XVGA 1024x768 \@60Hz
 *  Before you run this application program the picture (AVR32.bmp) into the flash
 *  at address 0x00400000. Use for instance the avr32program application for this purpose.
 *  avr32program program -F bin -O 0x00400000 -evfcfi@0 AVR32.bmp
 *  If this picture is not available a blank rectangle will be visible in the
 *  upper left corner. The pictures resolution is 320x240 (QVGA).
 *
 * - Compiler=           GNU GCC 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>150 MHz</i>
 *
 *  This example shows how to set up the LCD Controller in combination with an
 *  external analog monitor. Three configurations for the resoultions VGA 648x480, 
 *  SVGA 800x600 and XVGA 1024X768 are available. Check your monitor specifications
 *  if it supports one of these configurations. If not the parameters can be 
 *  adjusted (blanking, polarity ...).
 *  To choose a configuration uncomment one of the defines in the example code.
 *  #define CONF_640_480_60 -> VGA 640x480@60Hz
 *  #define CONF_800_600_60 -> SVGA 800x600@60Hz
 *  #define CONF_1024_768_60 -> XVGA 1024x768@60Hz
 *  Before you run this application program the picture (AVR32.bmp) into the flash 
 *  at address 0x00400000. Use for instance the avr32program application for this purpose.
 *  avr32program program -F bin -O 0x00400000 -evfcfi@0 AVR32.bmp
 *  If this picture is not available a blank rectangle will be visible in the 
 *  upper left corner. The pictures resolution is 320x240 (QVGA).
 *
 *  - \b SRC lcdc_vga_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 "board.h"
#include "lcdc.h"
#include <string.h>
#include <avr32/io.h>
#include "sdramc_at32ap7000.h"
#include "ap7_errno.h"
#include "print_funcs.h"
#include "pm_at32ap7000.h"
#include "bmp_lib.h"

/* The configurations available. Notation is
 * {horizontal size}_{vertical size}_{refresh rate}
 */
#define CONF_640_480_60    640
#define CONF_800_600_60    800
#define CONF_1024_768_60  1024
/* Select configuration */
#define VGA_CONFIGURATION CONF_800_600_60

/* Enable disable the display configurations here */

#define BITMAP_FILE_ADDRESS 0xA0400000

/* Refresh rate of SDRAM in number of bus clocks */
#define REFRESH_RATE 1170

volatile avr32_usart_t *usart = &AVR32_USART1;
int display_bm(lcdc_conf_t *lcdc_conf, void * file_start);

/*! \brief Configuration 640x480 \@60Hz */
static lcdc_conf_t lcdc_vga_std_conf = {

  .dmabaddr1= 0x10000000,
  .dmabaddr2= 0,
  .burst_length= 4,
  .xres= 640,
  .yres= 480,
  .set2dmode= LCDC_MODE_2D_OFF,
  .virtual_xres= 0,
  .virtual_yres= 0,
  .frame_rate= 60,
  .lcdcclock= 25000000,
  .guard_time= 2,
  .memor= LCDC_BIG_ENDIAN,
  .ifwidth= 0,
  .scanmod= LCDC_SINGLE_SCAN,
  .distype= LCDC_TFT,
  .invvd= LCDC_NORMAL,
  .invframe= LCDC_NORMAL,
  .invline= LCDC_NORMAL,
  .invclk= LCDC_INVERTED,
  .invdval= LCDC_NORMAL,
  .clkmod= LCDC_ALWAYS_ACTIVE,
  .pixelsize= LCDC_BPP_24,
  .ctrstval= 0x0f,
  .ctrst_ena= LCDC_DISABLED,
  .ctrst_pol= LCDC_NORMAL,
  .ctrst_ps= LCDC_PRE_HALF,
  .mval= 0,
  .mmode= LCDC_EACH_FRAME,
  .hpw= 64,/* Should be 96 but LCD Controller supports only up to 64 */
  .hbp= 48,
  .hfp= 16,
  .vpw= 2,
  .vbp= 33,
  .vfp= 10,
  .vhdly= 0,
};
/*! \brief Configuration VGA 800x600 \@60Hz */
static lcdc_conf_t lcdc_800_600_60_conf = {

  .dmabaddr1= 0x10000000,
  .dmabaddr2= 0,
  .burst_length= 4,
  .xres= 800,
  .yres= 600,
  .set2dmode= LCDC_MODE_2D_OFF,
  .virtual_xres= 0,
  .virtual_yres= 0,
  .frame_rate= 60,
  .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_NORMAL,
  .invclk= LCDC_INVERTED,
  .invdval= LCDC_NORMAL,
  .clkmod= LCDC_ALWAYS_ACTIVE,
  .pixelsize= LCDC_BPP_24,
  .ctrstval= 0x0f,
  .ctrst_ena= LCDC_DISABLED,
  .ctrst_pol= LCDC_NORMAL,
  .ctrst_ps= LCDC_PRE_HALF,
  .mval= 0,
  .mmode= LCDC_EACH_FRAME,
  .hpw= 64, /* should be 80 but LCD Controller supports only up to 64 */
  .hbp= 126,
  .hfp= 46,
  .vpw= 4,
  .vbp= 17,
  .vfp= 3,
  .vhdly= 0,
};
/*! \brief Configuration 1024x768 \@60Hz */
static lcdc_conf_t lcdc_1024_768_60_conf = {
  .dmabaddr1= 0x10000000,
  .dmabaddr2= 0,
  .burst_length= 4,
  .xres= 1024,
  .yres= 768,
  .set2dmode= LCDC_MODE_2D_OFF,
  .virtual_xres= 0,
  .virtual_yres= 0,
  .frame_rate= 60,
  .lcdcclock= 64000000,
  .guard_time= 2,
  .memor= LCDC_BIG_ENDIAN,
  .ifwidth= 0,
  .scanmod= LCDC_SINGLE_SCAN,
  .distype= LCDC_TFT,
  .invvd= LCDC_NORMAL,
  .invframe= LCDC_INVERTED,
  .invline= LCDC_NORMAL,
  .invclk= LCDC_INVERTED,
  .invdval= LCDC_NORMAL,
  .clkmod= LCDC_ALWAYS_ACTIVE,
  .pixelsize= LCDC_BPP_16,
  .ctrstval= 0x0f,
  .ctrst_ena= LCDC_DISABLED,
  .ctrst_pol= LCDC_NORMAL,
  .ctrst_ps= LCDC_PRE_HALF,
  .mval= 0,
  .mmode= LCDC_EACH_FRAME,
  .hpw= 64, /* Should be 104 but LCD controller supports only up to 64 */
  .hbp= 152,
  .hfp= 48,
  .vpw= 4,
  .vbp= 23,
  .vfp= 3,
  .vhdly= 0,
};

/*! \brief Sets up the pins for the LCD on the STK1000
 *
 */
void lcdc_pio_config(void){
  /* CC and MOD signals are not used for the video DAC and are not connected to
  * the monitor interface
  */
    static const gpio_map_t lcdc_pio_map = {
      { AVR32_LCDC_DVAL_0_0_PIN, AVR32_LCDC_DVAL_0_0_FUNCTION },
      { AVR32_LCDC_HSYNC_0_PIN, AVR32_LCDC_HSYNC_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(lcdc_pio_map, 31);
}

/*! \brief Fill the frame buffer with a test picture
 *
 * \param lcdc_conf Pointer to LCD configuration structure
 */
void fill_frame_buffer (lcdc_conf_t *lcdc_conf)
{
  unsigned int k,l,x,y;
  unsigned char * framePtr;

  framePtr = (unsigned char *) (lcdc_conf->dmabaddr1 | 0xA0000000);

  if(lcdc_conf->pixelsize == LCDC_BPP_24){
  for (l=0; l < lcdc_conf->yres; l++){
    for (k=0; k < lcdc_conf->xres; k++)
    {
      x = (256 * l) / lcdc_conf->yres;
      y = (256 * k) / lcdc_conf->xres;
      *framePtr++ = x;
      *framePtr++ = y;
      *framePtr++ = 256 - (x + y) / 2;
    }
  }
  }
  if(lcdc_conf->pixelsize == LCDC_BPP_16){
    for (l=0; l < lcdc_conf->yres; l++){
      for (k=0; k < lcdc_conf->xres; k++)
      {
        x = (32 * l) / lcdc_conf->yres;
        y = (32 * k) / lcdc_conf->xres;
        /* 1-5-5-5 format */
        *framePtr++ = (x << 2) | (y >> 3);
        *framePtr++ = (y << 5) | (32 - (x + y) / 2);
      }
    }
  }
}

int main (void)
{
  volatile struct avr32_smc_t *smc = &AVR32_SMC;
  lcdc_conf_t *lcdc_conf;

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

  /* set up SMC for higher frequencies */
  smc->cs[0].mode = 0x00031103;
  smc->cs[0].cycle = 0x000c000d;
  smc->cs[0].pulse = 0x0b0a0906;
  smc->cs[0].setup = 0x00010002;

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

  // Divide HSB by 2, PBB by 4 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, 4);
  pm_set_clock_domain_scaler(PM_PBA_DOMAIN, 4);

  pm_set_mclk_source(PM_PLL0);

  /* Initialize default debug usart */
  init_dbg_rs232(pm_read_module_freq_hz(PM_PBA_USART1));

  sdramc_init(pm_read_module_freq_hz(PM_PBB_HSDRAMC));

  switch (VGA_CONFIGURATION)
  {
  case CONF_640_480_60:
      lcdc_conf = &lcdc_vga_std_conf;
      /* 25MHz pixel clock */
      pll_opt.mul = 25;
      pll_opt.div = 12;
      break;
  case CONF_800_600_60:
      lcdc_conf = &lcdc_800_600_60_conf;
      /* 40 MHz pixel clock */
      pll_opt.mul = 10;
      pll_opt.div = 3;
      break;
  case CONF_1024_768_60:
      lcdc_conf = &lcdc_1024_768_60_conf;
      /* 63,5 MHz pixel clock */
      pll_opt.mul = 37;
      pll_opt.div = 7;
      break;
  default:
      print_dbg("Invalid or missing VGA configuration\n");
      break;
  }

  print_dbg( "Setting up PLL1 for LCD Controller\n");
  print_dbg(" -> PLL1 ... ");
  pll_opt.pll_id = 1;
  pll_opt.osc_id = 1;
  pll_opt.count = 16;
  pm_start_pll(&pll_opt);
  print_dbg("OK\n");

  /* clear framebuffer */
  memset((void *)lcdc_conf->dmabaddr1, 0, lcdc_conf->xres * lcdc_conf->yres * lcdc_conf->pixelsize / 8);

  fill_frame_buffer(lcdc_conf);
  
  display_bm(lcdc_conf, ((void *) BITMAP_FILE_ADDRESS));

  print_dbg("Setting up LCD controller\n");
  lcdc_pio_config();

  /* Power manager setup
  * Enable CLOCK for LCDC in HSBMASK
  */
  pm_enable_module(PM_HSB_LCDC);
  /* Feed generic clock for LCD Controller from PLL1 */
  pm_gen_clk_opt_t gen_clk_opt = {
    .clock_source = PM_PLL1,
    .divider = 0,
  };
  pm_start_generic_clock(7, &gen_clk_opt);

  lcdc_init(lcdc_conf);
  print_dbg("Setup complete\n");

  while(1);
}
